]> granicus.if.org Git - shadow/blob - libmisc/copydir.c
* po/POTFILES.in, libmisc/Makefile.am, lib/prototypes.h,
[shadow] / libmisc / copydir.c
1 /*
2  * Copyright (c) 1991 - 1994, Julianne Frances Haugh
3  * Copyright (c) 1996 - 2001, Marek Michałkiewicz
4  * Copyright (c) 2003 - 2006, Tomasz Kłoczko
5  * Copyright (c) 2007 - 2010, Nicolas François
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. The name of the copyright holders or contributors may not be used to
17  *    endorse or promote products derived from this software without
18  *    specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23  * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
24  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include <config.h>
34
35 #ident "$Id$"
36
37 #include <assert.h>
38 #include <sys/stat.h>
39 #include <sys/types.h>
40 #include <sys/time.h>
41 #include <fcntl.h>
42 #include <stdio.h>
43 #include "prototypes.h"
44 #include "defines.h"
45 #ifdef WITH_SELINUX
46 #include <selinux/selinux.h>
47 #endif                          /* WITH_SELINUX */
48 #if defined(WITH_ACL) || defined(WITH_ATTR)
49 #include <attr/error_context.h>
50 #endif                          /* WITH_ACL || WITH_ATTR */
51 #ifdef WITH_ACL
52 #include <acl/libacl.h>
53 #endif                          /* WITH_ACL */
54 #ifdef WITH_ATTR
55 #include <attr/libattr.h>
56 #endif                          /* WITH_ATTR */
57
58 static /*@null@*/const char *src_orig;
59 static /*@null@*/const char *dst_orig;
60
61 struct link_name {
62         dev_t ln_dev;
63         ino_t ln_ino;
64         nlink_t ln_count;
65         char *ln_name;
66         /*@dependent@*/struct link_name *ln_next;
67 };
68 static /*@exposed@*/struct link_name *links;
69
70 static int copy_entry (const char *src, const char *dst,
71                        long int uid, long int gid);
72 static int copy_dir (const char *src, const char *dst,
73                      const struct stat *statp, const struct timeval mt[],
74                      long int uid, long int gid);
75 #ifdef  S_IFLNK
76 static char *readlink_malloc (const char *filename);
77 static int copy_symlink (const char *src, const char *dst,
78                          const struct stat *statp, const struct timeval mt[],
79                          long int uid, long int gid);
80 #endif                          /* S_IFLNK */
81 static int copy_hardlink (const char *src, const char *dst,
82                           struct link_name *lp);
83 static int copy_special (const char *src, const char *dst,
84                          const struct stat *statp, const struct timeval mt[],
85                          long int uid, long int gid);
86 static int copy_file (const char *src, const char *dst,
87                       const struct stat *statp, const struct timeval mt[],
88                       long int uid, long int gid);
89
90 #ifdef WITH_SELINUX
91 /*
92  * selinux_file_context - Set the security context before any file or
93  *                        directory creation.
94  *
95  *      selinux_file_context () should be called before any creation of file,
96  *      symlink, directory, ...
97  *
98  *      Callers may have to Reset SELinux to create files with default
99  *      contexts:
100  *              setfscreatecon (NULL);
101  */
102 int selinux_file_context (const char *dst_name)
103 {
104         static bool selinux_checked = false;
105         static bool selinux_enabled;
106         security_context_t scontext = NULL;
107
108         if (!selinux_checked) {
109                 selinux_enabled = is_selinux_enabled () > 0;
110                 selinux_checked = true;
111         }
112
113         if (selinux_enabled) {
114                 /* Get the default security context for this file */
115                 if (matchpathcon (dst_name, 0, &scontext) < 0) {
116                         if (security_getenforce () != 0) {
117                                 return 1;
118                         }
119                 }
120                 /* Set the security context for the next created file */
121                 if (setfscreatecon (scontext) < 0) {
122                         if (security_getenforce () != 0) {
123                                 return 1;
124                         }
125                 }
126                 freecon (scontext);
127         }
128         return 0;
129 }
130 #endif                          /* WITH_SELINUX */
131
132 #if defined(WITH_ACL) || defined(WITH_ATTR)
133 void error (struct error_context *ctx, const char *fmt, ...)
134 {
135         va_list ap;
136
137         va_start (ap, fmt);
138         (void) fprintf (stderr, _("%s: "), Prog);
139         if (vfprintf (stderr, fmt, ap) != 0) {
140                 (void) fputs (_(": "), stderr);
141         }
142         (void) fprintf (stderr, "%s\n", strerror (errno));
143         va_end (ap);
144 }
145
146 struct error_context ctx = {
147         error
148 };
149 #endif                          /* WITH_ACL || WITH_ATTR */
150
151 /*
152  * remove_link - delete a link from the linked list
153  */
154 static void remove_link (/*@only@*/struct link_name *ln)
155 {
156         struct link_name *lp;
157
158         if (links == ln) {
159                 links = ln->ln_next;
160                 free (ln->ln_name);
161                 free (ln);
162                 return;
163         }
164         for (lp = links; NULL !=lp; lp = lp->ln_next) {
165                 if (lp->ln_next == ln) {
166                         break;
167                 }
168         }
169
170         if (NULL == lp) {
171                 free (ln->ln_name);
172                 free (ln);
173                 return;
174         }
175
176         lp->ln_next = lp->ln_next->ln_next;
177         free (ln->ln_name);
178         free (ln);
179 }
180
181 /*
182  * check_link - see if a file is really a link
183  */
184
185 static /*@exposed@*/ /*@null@*/struct link_name *check_link (const char *name, const struct stat *sb)
186 {
187         struct link_name *lp;
188         size_t src_len;
189         size_t dst_len;
190         size_t name_len;
191         size_t len;
192
193         /* copy_tree () must be the entry point */
194         assert (NULL != src_orig);
195         assert (NULL != dst_orig);
196
197         for (lp = links; NULL != lp; lp = lp->ln_next) {
198                 if ((lp->ln_dev == sb->st_dev) && (lp->ln_ino == sb->st_ino)) {
199                         return lp;
200                 }
201         }
202
203         if (sb->st_nlink == 1) {
204                 return NULL;
205         }
206
207         lp = (struct link_name *) xmalloc (sizeof *lp);
208         src_len = strlen (src_orig);
209         dst_len = strlen (dst_orig);
210         name_len = strlen (name);
211         lp->ln_dev = sb->st_dev;
212         lp->ln_ino = sb->st_ino;
213         lp->ln_count = sb->st_nlink;
214         len = name_len - src_len + dst_len + 1;
215         lp->ln_name = (char *) xmalloc (len);
216         snprintf (lp->ln_name, len, "%s%s", dst_orig, name + src_len);
217         lp->ln_next = links;
218         links = lp;
219
220         return NULL;
221 }
222
223 /*
224  * copy_tree - copy files in a directory tree
225  *
226  *      copy_tree() walks a directory tree and copies ordinary files
227  *      as it goes.
228  */
229 int copy_tree (const char *src_root, const char *dst_root,
230                long int uid, long int gid)
231 {
232         int err = 0;
233         bool set_orig = false;
234         struct DIRECT *ent;
235         DIR *dir;
236
237         /*
238          * Make certain both directories exist.  This routine is called
239          * after the home directory is created, or recursively after the
240          * target is created.  It assumes the target directory exists.
241          */
242
243         if (   (access (src_root, F_OK) != 0)
244             || (access (dst_root, F_OK) != 0)) {
245                 return -1;
246         }
247
248         /*
249          * Open the source directory and read each entry.  Every file
250          * entry in the directory is copied with the UID and GID set
251          * to the provided values.  As an added security feature only
252          * regular files (and directories ...) are copied, and no file
253          * is made set-ID.
254          */
255         dir = opendir (src_root);
256         if (NULL == dir) {
257                 return -1;
258         }
259
260         if (src_orig == NULL) {
261                 src_orig = src_root;
262                 dst_orig = dst_root;
263                 set_orig = true;
264         }
265         while ((0 == err) && (ent = readdir (dir)) != NULL) {
266                 /*
267                  * Skip the "." and ".." entries
268                  */
269                 if ((strcmp (ent->d_name, ".") != 0) &&
270                     (strcmp (ent->d_name, "..") != 0)) {
271                         char *src_name;
272                         char *dst_name;
273                         size_t src_len = strlen (ent->d_name) + 2;
274                         size_t dst_len = strlen (ent->d_name) + 2;
275                         src_len += strlen (src_root);
276                         dst_len += strlen (dst_root);
277
278                         src_name = (char *) malloc (src_len);
279                         dst_name = (char *) malloc (dst_len);
280
281                         if ((NULL == src_name) || (NULL == dst_name)) {
282                                 err = -1;
283                         } else {
284                                 /*
285                                  * Build the filename for both the source and
286                                  * the destination files.
287                                  */
288                                 snprintf (src_name, src_len, "%s/%s",
289                                           src_root, ent->d_name);
290                                 snprintf (dst_name, dst_len, "%s/%s",
291                                           dst_root, ent->d_name);
292
293                                 err = copy_entry (src_name, dst_name, uid, gid);
294                         }
295                         if (NULL != src_name) {
296                                 free (src_name);
297                         }
298                         if (NULL != dst_name) {
299                                 free (dst_name);
300                         }
301                 }
302         }
303         (void) closedir (dir);
304
305         if (set_orig) {
306                 src_orig = NULL;
307                 dst_orig = NULL;
308         }
309
310 #ifdef WITH_SELINUX
311         /* Reset SELinux to create files with default contexts */
312         setfscreatecon (NULL);
313 #endif                          /* WITH_SELINUX */
314
315         /* FIXME: with the call to remove_link, we could also check that
316          *        no links remain in links.
317          * assert (NULL == links); */
318
319         return err;
320 }
321
322 /*
323  * copy_entry - copy the entry of a directory
324  *
325  *      Copy the entry src to dst.
326  *      Depending on the type of entry, this function will forward the
327  *      request to copy_dir(), copy_symlink(), copy_hardlink(),
328  *      copy_special(), or copy_file().
329  *
330  *      The access and modification time will not be modified.
331  *
332  *      The permissions will be set to uid/gid.
333  *
334  *      If uid (resp. gid) is equal to -1, the user (resp. group) will
335  *      not be modified.
336  */
337 static int copy_entry (const char *src, const char *dst,
338                        long int uid, long int gid)
339 {
340         int err = 0;
341         struct stat sb;
342         struct link_name *lp;
343         struct timeval mt[2];
344
345         if (LSTAT (src, &sb) == -1) {
346                 /* If we cannot stat the file, do not care. */
347         } else {
348 #ifdef HAVE_STRUCT_STAT_ST_ATIM
349                 mt[0].tv_sec  = sb.st_atim.tv_sec;
350                 mt[0].tv_usec = sb.st_atim.tv_nsec / 1000;
351 #else                           /* !HAVE_STRUCT_STAT_ST_ATIM */
352                 mt[0].tv_sec  = sb.st_atime;
353 # ifdef HAVE_STRUCT_STAT_ST_ATIMENSEC
354                 mt[0].tv_usec = sb.st_atimensec / 1000;
355 # else                          /* !HAVE_STRUCT_STAT_ST_ATIMENSEC */
356                 mt[0].tv_usec = 0;
357 # endif                         /* !HAVE_STRUCT_STAT_ST_ATIMENSEC */
358 #endif                          /* !HAVE_STRUCT_STAT_ST_ATIM */
359
360 #ifdef HAVE_STRUCT_STAT_ST_MTIM
361                 mt[1].tv_sec  = sb.st_mtim.tv_sec;
362                 mt[1].tv_usec = sb.st_mtim.tv_nsec / 1000;
363 #else                           /* !HAVE_STRUCT_STAT_ST_MTIM */
364                 mt[1].tv_sec  = sb.st_mtime;
365 # ifdef HAVE_STRUCT_STAT_ST_MTIMENSEC
366                 mt[1].tv_usec = sb.st_mtimensec / 1000;
367 # else                          /* !HAVE_STRUCT_STAT_ST_MTIMENSEC */
368                 mt[1].tv_usec = 0;
369 # endif                         /* !HAVE_STRUCT_STAT_ST_MTIMENSEC */
370 #endif                          /* !HAVE_STRUCT_STAT_ST_MTIM */
371
372                 if (S_ISDIR (sb.st_mode)) {
373                         err = copy_dir (src, dst, &sb, mt, uid, gid);
374                 }
375
376 #ifdef  S_IFLNK
377                 /*
378                  * Copy any symbolic links
379                  */
380
381                 else if (S_ISLNK (sb.st_mode)) {
382                         err = copy_symlink (src, dst, &sb, mt, uid, gid);
383                 }
384 #endif                          /* S_IFLNK */
385
386                 /*
387                  * See if this is a previously copied link
388                  */
389
390                 else if ((lp = check_link (src, &sb)) != NULL) {
391                         err = copy_hardlink (src, dst, lp);
392                 }
393
394                 /*
395                  * Deal with FIFOs and special files.  The user really
396                  * shouldn't have any of these, but it seems like it
397                  * would be nice to copy everything ...
398                  */
399
400                 else if (!S_ISREG (sb.st_mode)) {
401                         err = copy_special (src, dst, &sb, mt, uid, gid);
402                 }
403
404                 /*
405                  * Create the new file and copy the contents.  The new
406                  * file will be owned by the provided UID and GID values.
407                  */
408
409                 else {
410                         err = copy_file (src, dst, &sb, mt, uid, gid);
411                 }
412         }
413
414         return err;
415 }
416
417 /*
418  * copy_dir - copy a directory
419  *
420  *      Copy a directory (recursively) from src to dst.
421  *
422  *      statp, mt, uid, gid are used to set the access and modification and the
423  *      access rights.
424  *
425  *      Return 0 on success, -1 on error.
426  */
427 static int copy_dir (const char *src, const char *dst,
428                      const struct stat *statp, const struct timeval mt[],
429                      long int uid, long int gid)
430 {
431         int err = 0;
432
433         /*
434          * Create a new target directory, make it owned by
435          * the user and then recursively copy that directory.
436          */
437
438 #ifdef WITH_SELINUX
439         selinux_file_context (dst);
440 #endif                          /* WITH_SELINUX */
441         if (   (mkdir (dst, statp->st_mode) != 0)
442             || (chown (dst,
443                        (uid == - 1) ? statp->st_uid : (uid_t) uid,
444                        (gid == - 1) ? statp->st_gid : (gid_t) gid) != 0)
445 #ifdef WITH_ACL
446             || (perm_copy_file (src, dst, &ctx) != 0)
447 #else                           /* !WITH_ACL */
448             || (chmod (dst, statp->st_mode) != 0)
449 #endif                          /* !WITH_ACL */
450 #ifdef WITH_ATTR
451         /*
452          * If the third parameter is NULL, all extended attributes
453          * except those that define Access Control Lists are copied.
454          * ACLs are excluded by default because copying them between
455          * file systems with and without ACL support needs some
456          * additional logic so that no unexpected permissions result.
457          */
458             || (attr_copy_file (src, dst, NULL, &ctx) != 0)
459 #endif                          /* WITH_ATTR */
460             || (copy_tree (src, dst, uid, gid) != 0)
461             || (utimes (dst, mt) != 0)) {
462                 err = -1;
463         }
464
465         return err;
466 }
467
468 #ifdef  S_IFLNK
469 /*
470  * readlink_malloc - wrapper for readlink
471  *
472  * return NULL on error.
473  * The return string shall be freed by the caller.
474  */
475 static char *readlink_malloc (const char *filename)
476 {
477         size_t size = 1024;
478
479         while (1) {
480                 ssize_t nchars;
481                 char *buffer = (char *) malloc (size);
482                 if (NULL == buffer) {
483                         return NULL;
484                 }
485
486                 nchars = readlink (filename, buffer, size);
487
488                 if (nchars < 0) {
489                         free(buffer);
490                         return NULL;
491                 }
492
493                 if ( (size_t) nchars < size) { /* The buffer was large enough */
494                         /* readlink does not nul-terminate */
495                         buffer[nchars] = '\0';
496                         return buffer;
497                 }
498
499                 /* Try again with a bigger buffer */
500                 free (buffer);
501                 size *= 2;
502         }
503 }
504
505 /*
506  * copy_symlink - copy a symlink
507  *
508  *      Copy a symlink from src to dst.
509  *
510  *      statp, mt, uid, gid are used to set the access and modification and the
511  *      access rights.
512  *
513  *      Return 0 on success, -1 on error.
514  */
515 static int copy_symlink (const char *src, const char *dst,
516                          const struct stat *statp, const struct timeval mt[],
517                          long int uid, long int gid)
518 {
519         char *oldlink;
520
521         /* copy_tree () must be the entry point */
522         assert (NULL != src_orig);
523         assert (NULL != dst_orig);
524
525         /*
526          * Get the name of the file which the link points
527          * to.  If that name begins with the original
528          * source directory name, that part of the link
529          * name will be replaced with the original
530          * destination directory name.
531          */
532
533         oldlink = readlink_malloc (src);
534         if (NULL == oldlink) {
535                 return -1;
536         }
537
538         /* If src was a link to an entry of the src_orig directory itself,
539          * create a link to the corresponding entry in the dst_orig
540          * directory.
541          * FIXME: This may change a relative link to an absolute link
542          */
543         if (strncmp (oldlink, src_orig, strlen (src_orig)) == 0) {
544                 size_t len = strlen (dst_orig) + strlen (oldlink) - strlen (src_orig) + 1;
545                 char *dummy = (char *) malloc (len);
546                 snprintf (dummy, len, "%s%s",
547                           dst_orig,
548                           oldlink + strlen (src_orig));
549                 free (oldlink);
550                 oldlink = dummy;
551         }
552
553 #ifdef WITH_SELINUX
554         selinux_file_context (dst);
555 #endif                          /* WITH_SELINUX */
556         if (   (symlink (oldlink, dst) != 0)
557             || (lchown (dst,
558                         (uid == -1) ? statp->st_uid : (uid_t) uid,
559                         (gid == -1) ? statp->st_gid : (gid_t) gid) != 0)) {
560                 /* FIXME: there are no modes on symlinks, right?
561                  *        ACL could be copied, but this would be much more
562                  *        complex than calling perm_copy_file.
563                  *        Ditto for Extended Attributes.
564                  *        We currently only document that ACL and Extended
565                  *        Attributes are not copied.
566                  */
567                 free (oldlink);
568                 return -1;
569         }
570         free (oldlink);
571
572 #ifdef HAVE_LUTIMES
573         /* 2007-10-18: We don't care about
574          *  exit status of lutimes because
575          *  it returns ENOSYS on many system
576          *  - not implemented
577          */
578         lutimes (dst, mt);
579 #endif                          /* HAVE_LUTIMES */
580
581         return 0;
582 }
583 #endif                          /* S_IFLNK */
584
585 /*
586  * copy_hardlink - copy a hardlink
587  *
588  *      Copy a hardlink from src to dst.
589  *
590  *      Return 0 on success, -1 on error.
591  */
592 static int copy_hardlink (const char *src, const char *dst,
593                           struct link_name *lp)
594 {
595         /* TODO: selinux, ACL, Extended Attributes needed? */
596
597         if (link (lp->ln_name, dst) != 0) {
598                 return -1;
599         }
600
601         /* FIXME: why is it unlinked? This is a copy, not a move */
602         if (unlink (src) != 0) {
603                 return -1;
604         }
605
606         /* FIXME: idem, although it may never be used again */
607         /* If the file could be unlinked, decrement the links counter,
608          * and delete the file if it was the last reference */
609         lp->ln_count--;
610         if (lp->ln_count <= 0) {
611                 remove_link (lp);
612         }
613
614         return 0;
615 }
616
617 /*
618  * copy_special - copy a special file
619  *
620  *      Copy a special file from src to dst.
621  *
622  *      statp, mt, uid, gid are used to set the access and modification and the
623  *      access rights.
624  *
625  *      Return 0 on success, -1 on error.
626  */
627 static int copy_special (const char *src, const char *dst,
628                          const struct stat *statp, const struct timeval mt[],
629                          long int uid, long int gid)
630 {
631         int err = 0;
632
633 #ifdef WITH_SELINUX
634         selinux_file_context (dst);
635 #endif                          /* WITH_SELINUX */
636
637         if (   (mknod (dst, statp->st_mode & ~07777, statp->st_rdev) != 0)
638             || (chown (dst,
639                        (uid == -1) ? statp->st_uid : (uid_t) uid,
640                        (gid == -1) ? statp->st_gid : (gid_t) gid) != 0)
641 #ifdef WITH_ACL
642             || (perm_copy_file (src, dst, &ctx) != 0)
643 #else                           /* !WITH_ACL */
644             || (chmod (dst, statp->st_mode & 07777) != 0)
645 #endif                          /* !WITH_ACL */
646 #ifdef WITH_ATTR
647         /*
648          * If the third parameter is NULL, all extended attributes
649          * except those that define Access Control Lists are copied.
650          * ACLs are excluded by default because copying them between
651          * file systems with and without ACL support needs some
652          * additional logic so that no unexpected permissions result.
653          */
654             || (attr_copy_file (src, dst, NULL, &ctx) != 0)
655 #endif                          /* WITH_ATTR */
656             || (utimes (dst, mt) != 0)) {
657                 err = -1;
658         }
659
660         return err;
661 }
662
663 /*
664  * copy_file - copy a file
665  *
666  *      Copy a file from src to dst.
667  *
668  *      statp, mt, uid, gid are used to set the access and modification and the
669  *      access rights.
670  *
671  *      Return 0 on success, -1 on error.
672  */
673 static int copy_file (const char *src, const char *dst,
674                       const struct stat *statp, const struct timeval mt[],
675                       long int uid, long int gid)
676 {
677         int err = 0;
678         int ifd;
679         int ofd;
680         char buf[1024];
681         ssize_t cnt;
682
683         ifd = open (src, O_RDONLY);
684         if (ifd < 0) {
685                 return -1;
686         }
687 #ifdef WITH_SELINUX
688         selinux_file_context (dst);
689 #endif                          /* WITH_SELINUX */
690         ofd = open (dst, O_WRONLY | O_CREAT | O_TRUNC, statp->st_mode & 07777);
691         if (   (ofd < 0)
692             || (fchown (ofd,
693                         (uid == -1) ? statp->st_uid : (uid_t) uid,
694                         (gid == -1) ? statp->st_gid : (gid_t) gid) != 0)
695 #ifdef WITH_ACL
696             || (perm_copy_fd (src, ifd, dst, ofd, &ctx) != 0)
697 #else                           /* !WITH_ACL */
698             || (fchmod (ofd, statp->st_mode & 07777) != 0)
699 #endif                          /* !WITH_ACL */
700 #ifdef WITH_ATTR
701         /*
702          * If the third parameter is NULL, all extended attributes
703          * except those that define Access Control Lists are copied.
704          * ACLs are excluded by default because copying them between
705          * file systems with and without ACL support needs some
706          * additional logic so that no unexpected permissions result.
707          */
708             || (attr_copy_fd (src, ifd, dst, ofd, NULL, &ctx) != 0)
709 #endif                          /* WITH_ATTR */
710            ) {
711                 (void) close (ifd);
712                 return -1;
713         }
714
715         while ((cnt = read (ifd, buf, sizeof buf)) > 0) {
716                 if (write (ofd, buf, (size_t)cnt) != cnt) {
717                         (void) close (ifd);
718                         return -1;
719                 }
720         }
721
722         (void) close (ifd);
723
724 #ifdef HAVE_FUTIMES
725         if (futimes (ofd, mt) != 0) {
726                 return -1;
727         }
728 #endif                          /* HAVE_FUTIMES */
729
730         if (close (ofd) != 0) {
731                 return -1;
732         }
733
734 #ifndef HAVE_FUTIMES
735         if (utimes(dst, mt) != 0) {
736                 return -1;
737         }
738 #endif                          /* !HAVE_FUTIMES */
739
740         return err;
741 }
742