]> granicus.if.org Git - shadow/blob - libmisc/copydir.c
Make sure every source files are distributed with a copyright and license.
[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 - 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$"
36
37 #include <sys/stat.h>
38 #include <sys/types.h>
39 #include <sys/time.h>
40 #include <fcntl.h>
41 #include <stdio.h>
42 #include "prototypes.h"
43 #include "defines.h"
44 #ifdef WITH_SELINUX
45 #include <selinux/selinux.h>
46 static int selinux_enabled = -1;
47 #endif
48 static const char *src_orig;
49 static const char *dst_orig;
50
51 struct link_name {
52         dev_t ln_dev;
53         ino_t ln_ino;
54         int ln_count;
55         char *ln_name;
56         struct link_name *ln_next;
57 };
58 static 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[2],
64                      long int uid, long int gid);
65 #ifdef  S_IFLNK
66 static int copy_symlink (const char *src, const char *dst,
67                          const struct stat *statp, const struct timeval mt[2],
68                          long int uid, long int gid);
69 #endif
70 static int copy_hardlink (const char *src, const char *dst,
71                           struct link_name *lp);
72 static int copy_special (const char *dst,
73                          const struct stat *statp, const struct timeval mt[2],
74                          long int uid, long int gid);
75 static int copy_file (const char *src, const char *dst,
76                       const struct stat *statp, const struct timeval mt[2],
77                       long int uid, long int gid);
78
79 #ifdef WITH_SELINUX
80 /*
81  * selinux_file_context - Set the security context before any file or
82  *                        directory creation.
83  *
84  *      selinux_file_context () should be called before any creation of file,
85  *      symlink, directory, ...
86  *
87  */
88 static int selinux_file_context (const char *dst_name)
89 {
90         security_context_t scontext = NULL;
91
92         if (selinux_enabled < 0) {
93                 selinux_enabled = is_selinux_enabled () > 0;
94         }
95         if (selinux_enabled) {
96                 /* Get the default security context for this file */
97                 if (matchpathcon (dst_name, 0, &scontext) < 0) {
98                         if (security_getenforce () != 0) {
99                                 return 1;
100                         }
101                 }
102                 /* Set the security context for the next created file */
103                 if (setfscreatecon (scontext) < 0) {
104                         if (security_getenforce () != 0) {
105                                 return 1;
106                         }
107                 }
108                 freecon (scontext);
109         }
110         return 0;
111 }
112 #endif
113
114 /*
115  * remove_link - delete a link from the linked list
116  */
117 static void remove_link (struct link_name *ln)
118 {
119         struct link_name *lp;
120
121         if (links == ln) {
122                 links = ln->ln_next;
123                 free (ln->ln_name);
124                 free (ln);
125                 return;
126         }
127         for (lp = links; NULL !=lp; lp = lp->ln_next) {
128                 if (lp->ln_next == ln) {
129                         break;
130                 }
131         }
132
133         if (NULL == lp) {
134                 return;
135         }
136
137         lp->ln_next = lp->ln_next->ln_next;
138         free (ln->ln_name);
139         free (ln);
140 }
141
142 /*
143  * check_link - see if a file is really a link
144  */
145
146 static struct link_name *check_link (const char *name, const struct stat *sb)
147 {
148         struct link_name *lp;
149         int src_len;
150         int dst_len;
151         int name_len;
152         int len;
153
154         for (lp = links; lp; lp = lp->ln_next) {
155                 if ((lp->ln_dev == sb->st_dev) && (lp->ln_ino == sb->st_ino)) {
156                         return lp;
157                 }
158         }
159
160         if (sb->st_nlink == 1) {
161                 return NULL;
162         }
163
164         lp = (struct link_name *) xmalloc (sizeof *lp);
165         src_len = strlen (src_orig);
166         dst_len = strlen (dst_orig);
167         name_len = strlen (name);
168         lp->ln_dev = sb->st_dev;
169         lp->ln_ino = sb->st_ino;
170         lp->ln_count = sb->st_nlink;
171         len = name_len - src_len + dst_len + 1;
172         lp->ln_name = xmalloc (len);
173         snprintf (lp->ln_name, len, "%s%s", dst_orig, name + src_len);
174         lp->ln_next = links;
175         links = lp;
176
177         return NULL;
178 }
179
180 /*
181  * copy_tree - copy files in a directory tree
182  *
183  *      copy_tree() walks a directory tree and copies ordinary files
184  *      as it goes.
185  */
186 int copy_tree (const char *src_root, const char *dst_root,
187                long int uid, long int gid)
188 {
189         char src_name[1024];
190         char dst_name[1024];
191         int err = 0;
192         int set_orig = 0;
193         struct DIRECT *ent;
194         DIR *dir;
195
196         /*
197          * Make certain both directories exist.  This routine is called
198          * after the home directory is created, or recursively after the
199          * target is created.  It assumes the target directory exists.
200          */
201
202         if (   (access (src_root, F_OK) != 0)
203             || (access (dst_root, F_OK) != 0)) {
204                 return -1;
205         }
206
207         /*
208          * Open the source directory and read each entry.  Every file
209          * entry in the directory is copied with the UID and GID set
210          * to the provided values.  As an added security feature only
211          * regular files (and directories ...) are copied, and no file
212          * is made set-ID.
213          */
214         dir = opendir (src_root);
215         if (NULL == dir) {
216                 return -1;
217         }
218
219         if (src_orig == 0) {
220                 src_orig = src_root;
221                 dst_orig = dst_root;
222                 set_orig++;
223         }
224         while ((0 == err) && (ent = readdir (dir)) != NULL) {
225                 /*
226                  * Skip the "." and ".." entries
227                  */
228                 if ((strcmp (ent->d_name, ".") != 0) &&
229                     (strcmp (ent->d_name, "..") != 0)) {
230                         /*
231                          * Make sure the resulting source and destination
232                          * filenames will fit in their buffers.
233                          */
234                         if (   (strlen (src_root) + strlen (ent->d_name) + 2 >
235                                 sizeof src_name)
236                             || (strlen (dst_root) + strlen (ent->d_name) + 2 >
237                                 sizeof dst_name)) {
238                                 err = -1;
239                         } else {
240                                 /*
241                                  * Build the filename for both the source and
242                                  * the destination files.
243                                  */
244                                 snprintf (src_name, sizeof src_name, "%s/%s",
245                                           src_root, ent->d_name);
246                                 snprintf (dst_name, sizeof dst_name, "%s/%s",
247                                           dst_root, ent->d_name);
248
249                                 err = copy_entry (src_name, dst_name, uid, gid);
250                         }
251                 }
252         }
253         closedir (dir);
254
255         if (set_orig) {
256                 src_orig = 0;
257                 dst_orig = 0;
258         }
259         return err;
260 }
261
262 /*
263  * copy_entry - copy the entry of a directory
264  *
265  *      Copy the entry src to dst.
266  *      Depending on the type of entry, this function will forward the
267  *      request to copy_dir(), copy_symlink(), copy_hardlink(),
268  *      copy_special(), or copy_file().
269  *
270  *      The access and modification time will not be modified.
271  *
272  *      The permissions will be set to uid/gid.
273  *
274  *      If uid (resp. gid) is equal to -1, the user (resp. group) will
275  *      not be modified.
276  */
277 static int copy_entry (const char *src, const char *dst,
278                        long int uid, long int gid)
279 {
280         int err = 0;
281         struct stat sb;
282         struct link_name *lp;
283         struct timeval mt[2];
284
285         if (LSTAT (src, &sb) == -1) {
286                 /* If we cannot stat the file, do not care. */
287         } else {
288 #if  defined(_BSD_SOURCE) || defined(_SVID_SOURCE)
289                 mt[0].tv_sec  = sb.st_atim.tv_sec;
290                 mt[0].tv_usec = sb.st_atim.tv_nsec / 1000;
291                 mt[1].tv_sec  = sb.st_mtim.tv_sec;
292                 mt[1].tv_usec = sb.st_mtim.tv_nsec / 1000;
293 #else
294                 mt[0].tv_sec  = sb.st_atime;
295                 mt[0].tv_usec = sb.st_atimensec / 1000;
296                 mt[1].tv_sec  = sb.st_mtime;
297                 mt[1].tv_usec = sb.st_mtimensec / 1000;
298 #endif
299
300                 if (S_ISDIR (sb.st_mode)) {
301                         err = copy_dir (src, dst, &sb, mt, uid, gid);
302                 }
303
304 #ifdef  S_IFLNK
305                 /*
306                  * Copy any symbolic links
307                  */
308
309                 else if (S_ISLNK (sb.st_mode)) {
310                         err = copy_symlink (src, dst, &sb, mt, uid, gid);
311                 }
312 #endif
313
314                 /*
315                  * See if this is a previously copied link
316                  */
317
318                 else if ((lp = check_link (src, &sb)) != NULL) {
319                         err = copy_hardlink (src, dst, lp);
320                 }
321
322                 /*
323                  * Deal with FIFOs and special files.  The user really
324                  * shouldn't have any of these, but it seems like it
325                  * would be nice to copy everything ...
326                  */
327
328                 else if (!S_ISREG (sb.st_mode)) {
329                         err = copy_special (dst, &sb, mt, uid, gid);
330                 }
331
332                 /*
333                  * Create the new file and copy the contents.  The new
334                  * file will be owned by the provided UID and GID values.
335                  */
336
337                 else {
338                         err = copy_file (src, dst, &sb, mt, uid, gid);
339                 }
340         }
341
342         return err;
343 }
344
345 /*
346  * copy_dir - copy a directory
347  *
348  *      Copy a directory (recursively) from src to dst.
349  *
350  *      statp, mt, uid, gid are used to set the access and modification and the
351  *      access rights.
352  *
353  *      Return 0 on success, -1 on error.
354  */
355 static int copy_dir (const char *src, const char *dst,
356                      const struct stat *statp, const struct timeval mt[2],
357                      long int uid, long int gid)
358 {
359         int err = 0;
360
361         /*
362          * Create a new target directory, make it owned by
363          * the user and then recursively copy that directory.
364          */
365
366 #ifdef WITH_SELINUX
367         selinux_file_context (dst);
368 #endif
369         if (   (mkdir (dst, statp->st_mode) != 0)
370             || (chown (dst,
371                        (uid == - 1) ? statp->st_uid : (uid_t) uid,
372                        (gid == - 1) ? statp->st_gid : (gid_t) gid) != 0)
373             || (chmod (dst, statp->st_mode) != 0)
374             || (copy_tree (src, dst, uid, gid) != 0)
375             || (utimes (dst, mt) != 0)) {
376                 err = -1;
377         }
378
379         return err;
380 }
381
382 #ifdef  S_IFLNK
383 /*
384  * copy_symlink - copy a symlink
385  *
386  *      Copy a symlink from src to dst.
387  *
388  *      statp, mt, uid, gid are used to set the access and modification and the
389  *      access rights.
390  *
391  *      Return 0 on success, -1 on error.
392  */
393 static int copy_symlink (const char *src, const char *dst,
394                          const struct stat *statp, const struct timeval mt[2],
395                          long int uid, long int gid)
396 {
397         char oldlink[1024];
398         char dummy[1024];
399         int len;
400         int err = 0;
401
402         /*
403          * Get the name of the file which the link points
404          * to.  If that name begins with the original
405          * source directory name, that part of the link
406          * name will be replaced with the original
407          * destination directory name.
408          */
409
410         len = readlink (src, oldlink, sizeof (oldlink) - 1);
411         if (len < 0) {
412                 return -1;
413         }
414         oldlink[len] = '\0';    /* readlink() does not NUL-terminate */
415         if (!strncmp (oldlink, src_orig, strlen (src_orig))) {
416                 snprintf (dummy, sizeof dummy, "%s%s",
417                           dst_orig,
418                           oldlink + strlen (src_orig));
419                 strcpy (oldlink, dummy);
420         }
421 #ifdef WITH_SELINUX
422         selinux_file_context (dst);
423 #endif
424         if (   (symlink (oldlink, dst) != 0)
425             || (lchown (dst,
426                         (uid == -1) ? statp->st_uid : (uid_t) uid,
427                         (gid == -1) ? statp->st_gid : (gid_t) gid) != 0)) {
428                 return -1;
429         }
430
431         /* 2007-10-18: We don't care about
432          *  exit status of lutimes because
433          *  it returns ENOSYS on many system
434          *  - not implemented
435          */
436         lutimes (dst, mt);
437
438         return err;
439 }
440 #endif
441
442 /*
443  * copy_hardlink - copy a hardlink
444  *
445  *      Copy a hardlink from src to dst.
446  *
447  *      Return 0 on success, -1 on error.
448  */
449 static int copy_hardlink (const char *src, const char *dst,
450                           struct link_name *lp)
451 {
452         /* TODO: selinux needed? */
453
454         if (link (lp->ln_name, dst) != 0) {
455                 return -1;
456         }
457         if (unlink (src) != 0) {
458                 return -1;
459         }
460
461         /* If the file could be unlinked, decrement the links counter,
462          * and delete the file if it was the last reference */
463         lp->ln_count--;
464         if (lp->ln_count <= 0) {
465                 remove_link (lp);
466         }
467
468         return 0;
469 }
470
471 /*
472  * copy_special - copy a special file
473  *
474  *      Copy a special file from src to dst.
475  *
476  *      statp, mt, uid, gid are used to set the access and modification and the
477  *      access rights.
478  *
479  *      Return 0 on success, -1 on error.
480  */
481 static int copy_special (const char *dst,
482                          const struct stat *statp, const struct timeval mt[2],
483                          long int uid, long int gid)
484 {
485         int err = 0;
486
487 #ifdef WITH_SELINUX
488         selinux_file_context (dst);
489 #endif
490
491         if (   (mknod (dst, statp->st_mode & ~07777, statp->st_rdev) != 0)
492             || (chown (dst,
493                        (uid == -1) ? statp->st_uid : (uid_t) uid,
494                        (gid == -1) ? statp->st_gid : (gid_t) gid) != 0)
495             || (chmod (dst, statp->st_mode & 07777) != 0)
496             || (utimes (dst, mt) != 0)) {
497                 err = -1;
498         }
499
500         return err;
501 }
502
503 /*
504  * copy_file - copy a file
505  *
506  *      Copy a file from src to dst.
507  *
508  *      statp, mt, uid, gid are used to set the access and modification and the
509  *      access rights.
510  *
511  *      Return 0 on success, -1 on error.
512  */
513 static int copy_file (const char *src, const char *dst,
514                       const struct stat *statp, const struct timeval mt[2],
515                       long int uid, long int gid)
516 {
517         int err = 0;
518         int ifd;
519         int ofd;
520         char buf[1024];
521         int cnt;
522
523         ifd = open (src, O_RDONLY);
524         if (ifd < 0) {
525                 return -1;
526         }
527 #ifdef WITH_SELINUX
528         selinux_file_context (dst);
529 #endif
530         ofd = open (dst, O_WRONLY | O_CREAT | O_TRUNC, 0);
531         if (   (ofd < 0)
532             || (chown (dst,
533                        (uid == -1) ? statp->st_uid : (uid_t) uid,
534                        (gid == -1) ? statp->st_gid : (gid_t) gid) != 0)
535             || (chmod (dst, statp->st_mode & 07777) != 0)) {
536                 close (ifd);
537                 return -1;
538         }
539
540         while ((cnt = read (ifd, buf, sizeof buf)) > 0) {
541                 if (write (ofd, buf, cnt) != cnt) {
542                         return -1;
543                 }
544         }
545
546         close (ifd);
547
548         if (futimes (ofd, mt) != 0) {
549                 return -1;
550         }
551
552         if (close (ofd) != 0) {
553                 return -1;
554         }
555
556         return err;
557 }
558
559 /*
560  * remove_tree - remove files in a directory tree
561  *
562  *      remove_tree() walks a directory tree and deletes all the files
563  *      and directories.
564  */
565
566 int remove_tree (const char *root)
567 {
568         char new_name[1024];
569         int err = 0;
570         struct DIRECT *ent;
571         struct stat sb;
572         DIR *dir;
573
574         /*
575          * Make certain the directory exists.
576          */
577
578         if (access (root, F_OK) != 0) {
579                 return -1;
580         }
581
582         /*
583          * Open the source directory and read each entry.  Every file
584          * entry in the directory is copied with the UID and GID set
585          * to the provided values.  As an added security feature only
586          * regular files (and directories ...) are copied, and no file
587          * is made set-ID.
588          */
589         dir = opendir (root);
590         if (NULL == dir) {
591                 return -1;
592         }
593
594         while ((ent = readdir (dir))) {
595
596                 /*
597                  * Skip the "." and ".." entries
598                  */
599
600                 if (strcmp (ent->d_name, ".") == 0 ||
601                     strcmp (ent->d_name, "..") == 0) {
602                         continue;
603                 }
604
605                 /*
606                  * Make the filename for the current entry.
607                  */
608
609                 if (strlen (root) + strlen (ent->d_name) + 2 > sizeof new_name) {
610                         err = -1;
611                         break;
612                 }
613                 snprintf (new_name, sizeof new_name, "%s/%s", root,
614                           ent->d_name);
615                 if (LSTAT (new_name, &sb) == -1) {
616                         continue;
617                 }
618
619                 if (S_ISDIR (sb.st_mode)) {
620
621                         /*
622                          * Recursively delete this directory.
623                          */
624
625                         if (remove_tree (new_name)) {
626                                 err = -1;
627                                 break;
628                         }
629                         if (rmdir (new_name)) {
630                                 err = -1;
631                                 break;
632                         }
633                         continue;
634                 }
635                 unlink (new_name);
636         }
637         closedir (dir);
638
639         return err;
640 }
641