Initial commit for Tizen
[profile/extras/shadow-utils.git] / 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 - 2008, 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: copydir.c 2978 2009-05-22 10:16:12Z nekral-guest $"
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
48 static /*@null@*/const char *src_orig;
49 static /*@null@*/const char *dst_orig;
50
51 struct link_name {
52         dev_t ln_dev;
53         ino_t ln_ino;
54         nlink_t ln_count;
55         char *ln_name;
56         /*@dependent@*/struct link_name *ln_next;
57 };
58 static /*@exposed@*/struct link_name *links;
59
60 static int copy_entry (const char *src, const char *dst,
61                        long int uid, long int gid);
62 static int copy_dir (const char *src, const char *dst,
63                      const struct stat *statp, const struct timeval mt[],
64                      long int uid, long int gid);
65 #ifdef  S_IFLNK
66 static char *readlink_malloc (const char *filename);
67 static int copy_symlink (const char *src, const char *dst,
68                          const struct stat *statp, const struct timeval mt[],
69                          long int uid, long int gid);
70 #endif
71 static int copy_hardlink (const char *src, const char *dst,
72                           struct link_name *lp);
73 static int copy_special (const char *dst,
74                          const struct stat *statp, const struct timeval mt[],
75                          long int uid, long int gid);
76 static int copy_file (const char *src, const char *dst,
77                       const struct stat *statp, const struct timeval mt[],
78                       long int uid, long int gid);
79
80 #ifdef WITH_SELINUX
81 /*
82  * selinux_file_context - Set the security context before any file or
83  *                        directory creation.
84  *
85  *      selinux_file_context () should be called before any creation of file,
86  *      symlink, directory, ...
87  *
88  *      Callers may have to Reset SELinux to create files with default
89  *      contexts:
90  *              setfscreatecon (NULL);
91  */
92 int selinux_file_context (const char *dst_name)
93 {
94         static bool selinux_checked = false;
95         static bool selinux_enabled;
96         security_context_t scontext = NULL;
97
98         if (!selinux_checked) {
99                 selinux_enabled = is_selinux_enabled () > 0;
100                 selinux_checked = true;
101         }
102
103         if (selinux_enabled) {
104                 /* Get the default security context for this file */
105                 if (matchpathcon (dst_name, 0, &scontext) < 0) {
106                         if (security_getenforce () != 0) {
107                                 return 1;
108                         }
109                 }
110                 /* Set the security context for the next created file */
111                 if (setfscreatecon (scontext) < 0) {
112                         if (security_getenforce () != 0) {
113                                 return 1;
114                         }
115                 }
116                 freecon (scontext);
117         }
118         return 0;
119 }
120 #endif
121
122 /*
123  * remove_link - delete a link from the linked list
124  */
125 static void remove_link (/*@only@*/struct link_name *ln)
126 {
127         struct link_name *lp;
128
129         if (links == ln) {
130                 links = ln->ln_next;
131                 free (ln->ln_name);
132                 free (ln);
133                 return;
134         }
135         for (lp = links; NULL !=lp; lp = lp->ln_next) {
136                 if (lp->ln_next == ln) {
137                         break;
138                 }
139         }
140
141         if (NULL == lp) {
142                 free (ln->ln_name);
143                 free (ln);
144                 return;
145         }
146
147         lp->ln_next = lp->ln_next->ln_next;
148         free (ln->ln_name);
149         free (ln);
150 }
151
152 /*
153  * check_link - see if a file is really a link
154  */
155
156 static /*@exposed@*/ /*@null@*/struct link_name *check_link (const char *name, const struct stat *sb)
157 {
158         struct link_name *lp;
159         size_t src_len;
160         size_t dst_len;
161         size_t name_len;
162         size_t len;
163
164         /* copy_tree () must be the entry point */
165         assert (NULL != src_orig);
166         assert (NULL != dst_orig);
167
168         for (lp = links; NULL != lp; lp = lp->ln_next) {
169                 if ((lp->ln_dev == sb->st_dev) && (lp->ln_ino == sb->st_ino)) {
170                         return lp;
171                 }
172         }
173
174         if (sb->st_nlink == 1) {
175                 return NULL;
176         }
177
178         lp = (struct link_name *) xmalloc (sizeof *lp);
179         src_len = strlen (src_orig);
180         dst_len = strlen (dst_orig);
181         name_len = strlen (name);
182         lp->ln_dev = sb->st_dev;
183         lp->ln_ino = sb->st_ino;
184         lp->ln_count = sb->st_nlink;
185         len = name_len - src_len + dst_len + 1;
186         lp->ln_name = (char *) xmalloc (len);
187         snprintf (lp->ln_name, len, "%s%s", dst_orig, name + src_len);
188         lp->ln_next = links;
189         links = lp;
190
191         return NULL;
192 }
193
194 /*
195  * copy_tree - copy files in a directory tree
196  *
197  *      copy_tree() walks a directory tree and copies ordinary files
198  *      as it goes.
199  */
200 int copy_tree (const char *src_root, const char *dst_root,
201                long int uid, long int gid)
202 {
203         int err = 0;
204         bool set_orig = false;
205         struct DIRECT *ent;
206         DIR *dir;
207
208         /*
209          * Make certain both directories exist.  This routine is called
210          * after the home directory is created, or recursively after the
211          * target is created.  It assumes the target directory exists.
212          */
213
214         if (   (access (src_root, F_OK) != 0)
215             || (access (dst_root, F_OK) != 0)) {
216                 return -1;
217         }
218
219         /*
220          * Open the source directory and read each entry.  Every file
221          * entry in the directory is copied with the UID and GID set
222          * to the provided values.  As an added security feature only
223          * regular files (and directories ...) are copied, and no file
224          * is made set-ID.
225          */
226         dir = opendir (src_root);
227         if (NULL == dir) {
228                 return -1;
229         }
230
231         if (src_orig == NULL) {
232                 src_orig = src_root;
233                 dst_orig = dst_root;
234                 set_orig = true;
235         }
236         while ((0 == err) && (ent = readdir (dir)) != NULL) {
237                 /*
238                  * Skip the "." and ".." entries
239                  */
240                 if ((strcmp (ent->d_name, ".") != 0) &&
241                     (strcmp (ent->d_name, "..") != 0)) {
242                         char *src_name;
243                         char *dst_name;
244                         size_t src_len = strlen (ent->d_name) + 2;
245                         size_t dst_len = strlen (ent->d_name) + 2;
246                         src_len += strlen (src_root);
247                         dst_len += strlen (dst_root);
248
249                         src_name = (char *) malloc (src_len);
250                         dst_name = (char *) malloc (dst_len);
251
252                         if ((NULL == src_name) || (NULL == dst_name)) {
253                                 err = -1;
254                         } else {
255                                 /*
256                                  * Build the filename for both the source and
257                                  * the destination files.
258                                  */
259                                 snprintf (src_name, src_len, "%s/%s",
260                                           src_root, ent->d_name);
261                                 snprintf (dst_name, dst_len, "%s/%s",
262                                           dst_root, ent->d_name);
263
264                                 err = copy_entry (src_name, dst_name, uid, gid);
265                         }
266                         if (NULL != src_name) {
267                                 free (src_name);
268                         }
269                         if (NULL != dst_name) {
270                                 free (dst_name);
271                         }
272                 }
273         }
274         (void) closedir (dir);
275
276         if (set_orig) {
277                 src_orig = NULL;
278                 dst_orig = NULL;
279         }
280
281 #ifdef WITH_SELINUX
282         /* Reset SELinux to create files with default contexts */
283         setfscreatecon (NULL);
284 #endif
285
286         /* FIXME: with the call to remove_link, we could also check that
287          *        no links remain in links.
288          * assert (NULL == links); */
289
290         return err;
291 }
292
293 /*
294  * copy_entry - copy the entry of a directory
295  *
296  *      Copy the entry src to dst.
297  *      Depending on the type of entry, this function will forward the
298  *      request to copy_dir(), copy_symlink(), copy_hardlink(),
299  *      copy_special(), or copy_file().
300  *
301  *      The access and modification time will not be modified.
302  *
303  *      The permissions will be set to uid/gid.
304  *
305  *      If uid (resp. gid) is equal to -1, the user (resp. group) will
306  *      not be modified.
307  */
308 static int copy_entry (const char *src, const char *dst,
309                        long int uid, long int gid)
310 {
311         int err = 0;
312         struct stat sb;
313         struct link_name *lp;
314         struct timeval mt[2];
315
316         if (LSTAT (src, &sb) == -1) {
317                 /* If we cannot stat the file, do not care. */
318         } else {
319 #ifdef HAVE_STRUCT_STAT_ST_ATIM
320                 mt[0].tv_sec  = sb.st_atim.tv_sec;
321                 mt[0].tv_usec = sb.st_atim.tv_nsec / 1000;
322 #else
323                 mt[0].tv_sec  = sb.st_atime;
324 #ifdef HAVE_STRUCT_STAT_ST_ATIMENSEC
325                 mt[0].tv_usec = sb.st_atimensec / 1000;
326 #else
327                 mt[0].tv_usec = 0;
328 #endif
329 #endif
330
331 #ifdef HAVE_STRUCT_STAT_ST_MTIM
332                 mt[1].tv_sec  = sb.st_mtim.tv_sec;
333                 mt[1].tv_usec = sb.st_mtim.tv_nsec / 1000;
334 #else
335                 mt[1].tv_sec  = sb.st_mtime;
336 #ifdef HAVE_STRUCT_STAT_ST_MTIMENSEC
337                 mt[1].tv_usec = sb.st_mtimensec / 1000;
338 #else
339                 mt[1].tv_usec = 0;
340 #endif
341 #endif
342
343                 if (S_ISDIR (sb.st_mode)) {
344                         err = copy_dir (src, dst, &sb, mt, uid, gid);
345                 }
346
347 #ifdef  S_IFLNK
348                 /*
349                  * Copy any symbolic links
350                  */
351
352                 else if (S_ISLNK (sb.st_mode)) {
353                         err = copy_symlink (src, dst, &sb, mt, uid, gid);
354                 }
355 #endif
356
357                 /*
358                  * See if this is a previously copied link
359                  */
360
361                 else if ((lp = check_link (src, &sb)) != NULL) {
362                         err = copy_hardlink (src, dst, lp);
363                 }
364
365                 /*
366                  * Deal with FIFOs and special files.  The user really
367                  * shouldn't have any of these, but it seems like it
368                  * would be nice to copy everything ...
369                  */
370
371                 else if (!S_ISREG (sb.st_mode)) {
372                         err = copy_special (dst, &sb, mt, uid, gid);
373                 }
374
375                 /*
376                  * Create the new file and copy the contents.  The new
377                  * file will be owned by the provided UID and GID values.
378                  */
379
380                 else {
381                         err = copy_file (src, dst, &sb, mt, uid, gid);
382                 }
383         }
384
385         return err;
386 }
387
388 /*
389  * copy_dir - copy a directory
390  *
391  *      Copy a directory (recursively) from src to dst.
392  *
393  *      statp, mt, uid, gid are used to set the access and modification and the
394  *      access rights.
395  *
396  *      Return 0 on success, -1 on error.
397  */
398 static int copy_dir (const char *src, const char *dst,
399                      const struct stat *statp, const struct timeval mt[],
400                      long int uid, long int gid)
401 {
402         int err = 0;
403
404         /*
405          * Create a new target directory, make it owned by
406          * the user and then recursively copy that directory.
407          */
408
409 #ifdef WITH_SELINUX
410         selinux_file_context (dst);
411 #endif
412         if (   (mkdir (dst, statp->st_mode) != 0)
413             || (chown (dst,
414                        (uid == - 1) ? statp->st_uid : (uid_t) uid,
415                        (gid == - 1) ? statp->st_gid : (gid_t) gid) != 0)
416             || (chmod (dst, statp->st_mode) != 0)
417             || (copy_tree (src, dst, uid, gid) != 0)
418             || (utimes (dst, mt) != 0)) {
419                 err = -1;
420         }
421
422         return err;
423 }
424
425 #ifdef  S_IFLNK
426 /*
427  * readlink_malloc - wrapper for readlink
428  *
429  * return NULL on error.
430  * The return string shall be freed by the caller.
431  */
432 static char *readlink_malloc (const char *filename)
433 {
434         size_t size = 1024;
435
436         while (1) {
437                 ssize_t nchars;
438                 char *buffer = (char *) malloc (size);
439                 if (NULL == buffer) {
440                         return NULL;
441                 }
442
443                 nchars = readlink (filename, buffer, size);
444
445                 if (nchars < 0) {
446                         return NULL;
447                 }
448
449                 if ( (size_t) nchars < size) { /* The buffer was large enough */
450                         /* readlink does not nul-terminate */
451                         buffer[nchars] = '\0';
452                         return buffer;
453                 }
454
455                 /* Try again with a bigger buffer */
456                 free (buffer);
457                 size *= 2;
458         }
459 }
460
461 /*
462  * copy_symlink - copy a symlink
463  *
464  *      Copy a symlink from src to dst.
465  *
466  *      statp, mt, uid, gid are used to set the access and modification and the
467  *      access rights.
468  *
469  *      Return 0 on success, -1 on error.
470  */
471 static int copy_symlink (const char *src, const char *dst,
472                          const struct stat *statp, const struct timeval mt[],
473                          long int uid, long int gid)
474 {
475         char *oldlink;
476
477         /* copy_tree () must be the entry point */
478         assert (NULL != src_orig);
479         assert (NULL != dst_orig);
480
481         /*
482          * Get the name of the file which the link points
483          * to.  If that name begins with the original
484          * source directory name, that part of the link
485          * name will be replaced with the original
486          * destination directory name.
487          */
488
489         oldlink = readlink_malloc (src);
490         if (NULL == oldlink) {
491                 return -1;
492         }
493
494         /* If src was a link to an entry of the src_orig directory itself,
495          * create a link to the corresponding entry in the dst_orig
496          * directory.
497          * FIXME: This may change a relative link to an absolute link
498          */
499         if (strncmp (oldlink, src_orig, strlen (src_orig)) == 0) {
500                 size_t len = strlen (dst_orig) + strlen (oldlink) - strlen (src_orig) + 1;
501                 char *dummy = (char *) malloc (len);
502                 snprintf (dummy, len, "%s%s",
503                           dst_orig,
504                           oldlink + strlen (src_orig));
505                 free (oldlink);
506                 oldlink = dummy;
507         }
508
509 #ifdef WITH_SELINUX
510         selinux_file_context (dst);
511 #endif
512         if (   (symlink (oldlink, dst) != 0)
513             || (lchown (dst,
514                         (uid == -1) ? statp->st_uid : (uid_t) uid,
515                         (gid == -1) ? statp->st_gid : (gid_t) gid) != 0)) {
516                 free (oldlink);
517                 return -1;
518         }
519         free (oldlink);
520
521 #ifdef HAVE_LUTIMES
522         /* 2007-10-18: We don't care about
523          *  exit status of lutimes because
524          *  it returns ENOSYS on many system
525          *  - not implemented
526          */
527         lutimes (dst, mt);
528 #endif
529
530         return 0;
531 }
532 #endif
533
534 /*
535  * copy_hardlink - copy a hardlink
536  *
537  *      Copy a hardlink from src to dst.
538  *
539  *      Return 0 on success, -1 on error.
540  */
541 static int copy_hardlink (const char *src, const char *dst,
542                           struct link_name *lp)
543 {
544         /* TODO: selinux needed? */
545
546         if (link (lp->ln_name, dst) != 0) {
547                 return -1;
548         }
549
550         /* FIXME: why is it unlinked? This is a copy, not a move */
551         if (unlink (src) != 0) {
552                 return -1;
553         }
554
555         /* FIXME: idem, although it may never be used again */
556         /* If the file could be unlinked, decrement the links counter,
557          * and delete the file if it was the last reference */
558         lp->ln_count--;
559         if (lp->ln_count <= 0) {
560                 remove_link (lp);
561         }
562
563         return 0;
564 }
565
566 /*
567  * copy_special - copy a special file
568  *
569  *      Copy a special file from src to dst.
570  *
571  *      statp, mt, uid, gid are used to set the access and modification and the
572  *      access rights.
573  *
574  *      Return 0 on success, -1 on error.
575  */
576 static int copy_special (const char *dst,
577                          const struct stat *statp, const struct timeval mt[],
578                          long int uid, long int gid)
579 {
580         int err = 0;
581
582 #ifdef WITH_SELINUX
583         selinux_file_context (dst);
584 #endif
585
586         if (   (mknod (dst, statp->st_mode & ~07777, statp->st_rdev) != 0)
587             || (chown (dst,
588                        (uid == -1) ? statp->st_uid : (uid_t) uid,
589                        (gid == -1) ? statp->st_gid : (gid_t) gid) != 0)
590             || (chmod (dst, statp->st_mode & 07777) != 0)
591             || (utimes (dst, mt) != 0)) {
592                 err = -1;
593         }
594
595         return err;
596 }
597
598 /*
599  * copy_file - copy a file
600  *
601  *      Copy a file from src to dst.
602  *
603  *      statp, mt, uid, gid are used to set the access and modification and the
604  *      access rights.
605  *
606  *      Return 0 on success, -1 on error.
607  */
608 static int copy_file (const char *src, const char *dst,
609                       const struct stat *statp, const struct timeval mt[],
610                       long int uid, long int gid)
611 {
612         int err = 0;
613         int ifd;
614         int ofd;
615         char buf[1024];
616         ssize_t cnt;
617
618         ifd = open (src, O_RDONLY);
619         if (ifd < 0) {
620                 return -1;
621         }
622 #ifdef WITH_SELINUX
623         selinux_file_context (dst);
624 #endif
625         ofd = open (dst, O_WRONLY | O_CREAT | O_TRUNC, statp->st_mode & 07777);
626         if (   (ofd < 0)
627             || (fchown (ofd,
628                         (uid == -1) ? statp->st_uid : (uid_t) uid,
629                         (gid == -1) ? statp->st_gid : (gid_t) gid) != 0)
630             || (fchmod (ofd, statp->st_mode & 07777) != 0)) {
631                 (void) close (ifd);
632                 return -1;
633         }
634
635         while ((cnt = read (ifd, buf, sizeof buf)) > 0) {
636                 if (write (ofd, buf, (size_t)cnt) != cnt) {
637                         return -1;
638                 }
639         }
640
641         (void) close (ifd);
642
643 #ifdef HAVE_FUTIMES
644         if (futimes (ofd, mt) != 0) {
645                 return -1;
646         }
647 #endif
648
649         if (close (ofd) != 0) {
650                 return -1;
651         }
652
653 #ifndef HAVE_FUTIMES
654         if (utimes(dst, mt) != 0) {
655                 return -1;
656         }
657 #endif
658
659         return err;
660 }
661
662 /*
663  * remove_tree - delete a directory tree
664  *
665  *      remove_tree() walks a directory tree and deletes all the files
666  *      and directories.
667  *      At the end, it deletes the root directory itself.
668  */
669
670 int remove_tree (const char *root)
671 {
672         char *new_name = NULL;
673         int err = 0;
674         struct DIRECT *ent;
675         struct stat sb;
676         DIR *dir;
677
678         /*
679          * Open the source directory and read each entry.  Every file
680          * entry in the directory is copied with the UID and GID set
681          * to the provided values.  As an added security feature only
682          * regular files (and directories ...) are copied, and no file
683          * is made set-ID.
684          */
685         dir = opendir (root);
686         if (NULL == dir) {
687                 return -1;
688         }
689
690         while ((ent = readdir (dir))) {
691                 size_t new_len = strlen (root) + strlen (ent->d_name) + 2;
692
693                 /*
694                  * Skip the "." and ".." entries
695                  */
696
697                 if (strcmp (ent->d_name, ".") == 0 ||
698                     strcmp (ent->d_name, "..") == 0) {
699                         continue;
700                 }
701
702                 /*
703                  * Make the filename for the current entry.
704                  */
705
706                 if (NULL != new_name) {
707                         free (new_name);
708                 }
709                 new_name = (char *) malloc (new_len);
710                 if (NULL == new_name) {
711                         err = -1;
712                         break;
713                 }
714                 snprintf (new_name, new_len, "%s/%s", root, ent->d_name);
715                 if (LSTAT (new_name, &sb) == -1) {
716                         continue;
717                 }
718
719                 if (S_ISDIR (sb.st_mode)) {
720                         /*
721                          * Recursively delete this directory.
722                          */
723                         if (remove_tree (new_name) != 0) {
724                                 err = -1;
725                                 break;
726                         }
727                 } else {
728                         /*
729                          * Delete the file.
730                          */
731                         if (unlink (new_name) != 0) {
732                                 err = -1;
733                                 break;
734                         }
735                 }
736         }
737         if (NULL != new_name) {
738                 free (new_name);
739         }
740         (void) closedir (dir);
741
742         if (0 == err) {
743                 if (rmdir (root) != 0) {
744                         err = -1;
745                 }
746         }
747
748         return err;
749 }
750