Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:19

0001 /* $NetBSD: utils.c,v 1.29 2005/10/15 18:22:18 christos Exp $ */
0002 
0003 /*-
0004  * Copyright (c) 1991, 1993, 1994
0005  *  The Regents of the University of California.  All rights reserved.
0006  *
0007  * Redistribution and use in source and binary forms, with or without
0008  * modification, are permitted provided that the following conditions
0009  * are met:
0010  * 1. Redistributions of source code must retain the above copyright
0011  *    notice, this list of conditions and the following disclaimer.
0012  * 2. Redistributions in binary form must reproduce the above copyright
0013  *    notice, this list of conditions and the following disclaimer in the
0014  *    documentation and/or other materials provided with the distribution.
0015  * 3. Neither the name of the University nor the names of its contributors
0016  *    may be used to endorse or promote products derived from this software
0017  *    without specific prior written permission.
0018  *
0019  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
0020  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0021  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0022  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
0023  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
0024  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
0025  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
0026  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
0027  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
0028  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0029  * SUCH DAMAGE.
0030  */
0031 
0032 #ifdef HAVE_CONFIG_H
0033 #include "config.h"
0034 #endif
0035 
0036 #include <sys/cdefs.h>
0037 #if 0
0038 #ifndef lint
0039 #if 0
0040 static char sccsid[] = "@(#)utils.c 8.3 (Berkeley) 4/1/94";
0041 #else
0042 __RCSID("$NetBSD: utils.c,v 1.29 2005/10/15 18:22:18 christos Exp $");
0043 #endif
0044 #endif /* not lint */
0045 #endif
0046 
0047 #if 0
0048 #include <sys/mman.h>
0049 #endif
0050 #include <sys/param.h>
0051 #include <sys/stat.h>
0052 #include <sys/time.h>
0053 #include <utime.h>
0054 
0055 #include "err.h"
0056 #include <errno.h>
0057 #include <fcntl.h>
0058 #include "fts.h"
0059 #include <limits.h>
0060 #include <stdio.h>
0061 #include <stdlib.h>
0062 #include <string.h>
0063 #include <unistd.h>
0064 
0065 #include "extern-cp.h"
0066 
0067 #define lchmod  chmod
0068 #define lchown  chown
0069 
0070 #define cp_pct(x, y)    ((y == 0) ? 0 : (int)(100.0 * (x) / (y)))
0071 
0072 int
0073 set_utimes(const char *file, struct stat *fs)
0074 {
0075   struct utimbuf tv;
0076 
0077   tv.actime = fs->st_atime;
0078   tv.modtime = fs->st_mtime;
0079 
0080   if (utime(file, &tv)) {
0081     warn("lutimes: %s", file);
0082     return (1);
0083   }
0084   return (0);
0085 }
0086 
0087 int
0088 copy_file(rtems_shell_cp_globals* cp_globals RTEMS_UNUSED, FTSENT *entp, int dne)
0089 {
0090 #define MAX_READ max_read
0091   int max_read;
0092     char* buf;
0093     struct stat *fs;
0094     ssize_t wcount;
0095     size_t wresid;
0096     off_t wtotal;
0097     int ch, checkch, from_fd = 0, rcount, rval, to_fd = 0;
0098     char *bufp;
0099 #ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED
0100     char *p;
0101 #endif
0102 
0103     fs = entp->fts_statp;
0104 
0105   max_read = fs->st_blksize;
0106   if (max_read < (8 * 1024))
0107     max_read = 8 * 1024;
0108   buf = malloc (max_read);
0109   if (!buf)
0110   {
0111         warn("no memory");
0112     return (1);
0113   }
0114 
0115     if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) {
0116         warn("%s", entp->fts_path);
0117     (void)free(buf);
0118         return (1);
0119     }
0120 
0121     /*
0122      * If the file exists and we're interactive, verify with the user.
0123      * If the file DNE, set the mode to be the from file, minus setuid
0124      * bits, modified by the umask; arguably wrong, but it makes copying
0125      * executables work right and it's been that way forever.  (The
0126      * other choice is 666 or'ed with the execute bits on the from file
0127      * modified by the umask.)
0128      */
0129     if (!dne) {
0130 #define YESNO "(y/n [n]) "
0131         if (nflag) {
0132             if (vflag)
0133                 printf("%s not overwritten\n", to.p_path);
0134             (void)close(from_fd);
0135       (void)free(buf);
0136             return (0);
0137         } else if (iflag) {
0138             (void)fprintf(stderr, "overwrite %s? %s",
0139                     to.p_path, YESNO);
0140             checkch = ch = getchar();
0141             while (ch != '\n' && ch != EOF)
0142                 ch = getchar();
0143             if (checkch != 'y' && checkch != 'Y') {
0144                 (void)close(from_fd);
0145         (void)free(buf);
0146                 (void)fprintf(stderr, "not overwritten\n");
0147                 return (1);
0148             }
0149         }
0150 
0151         if (fflag) {
0152             /* remove existing destination file name,
0153              * create a new file  */
0154             (void)unlink(to.p_path);
0155                 if (!lflag)
0156                 to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
0157                   fs->st_mode & ~(S_ISUID | S_ISGID));
0158         } else {
0159                 if (!lflag)
0160                 /* overwrite existing destination file name */
0161                 to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0);
0162         }
0163     } else {
0164         if (!lflag)
0165             to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT,
0166           fs->st_mode & ~(S_ISUID | S_ISGID));
0167     }
0168 
0169     if (to_fd == -1) {
0170         warn("%s", to.p_path);
0171         (void)close(from_fd);
0172     (void)free(buf);
0173         return (1);
0174     }
0175 
0176     rval = 0;
0177 
0178     if (!lflag) {
0179         /*
0180          * Mmap and write if less than 8M (the limit is so we don't totally
0181          * trash memory on big files.  This is really a minor hack, but it
0182          * wins some CPU back.
0183          */
0184 #ifdef CCJ_REMOVED_VM_AND_BUFFER_CACHE_SYNCHRONIZED
0185         if (S_ISREG(fs->st_mode) && fs->st_size > 0 &&
0186             fs->st_size <= 8 * 1048576) {
0187             if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ,
0188                 MAP_SHARED, from_fd, (off_t)0)) == MAP_FAILED) {
0189                 warn("%s", entp->fts_path);
0190                 rval = 1;
0191             } else {
0192                 wtotal = 0;
0193                 for (bufp = p, wresid = fs->st_size; ;
0194                     bufp += wcount, wresid -= (size_t)wcount) {
0195                     wcount = write(to_fd, bufp, wresid);
0196                     if (wcount <= 0)
0197                         break;
0198                     wtotal += wcount;
0199                     if (info) {
0200                         info = 0;
0201                         (void)fprintf(stderr,
0202                             "%s -> %s %3d%%\n",
0203                             entp->fts_path, to.p_path,
0204                             cp_pct(wtotal, fs->st_size));
0205                     }
0206                     if (wcount >= (ssize_t)wresid)
0207                         break;
0208                 }
0209                 if (wcount != (ssize_t)wresid) {
0210                     warn("%s", to.p_path);
0211                     rval = 1;
0212                 }
0213                 /* Some systems don't unmap on close(2). */
0214                 if (munmap(p, fs->st_size) < 0) {
0215                     warn("%s", entp->fts_path);
0216                     rval = 1;
0217                 }
0218             }
0219         } else
0220 #endif
0221         {
0222             wtotal = 0;
0223             while ((rcount = read(from_fd, buf, MAX_READ)) > 0) {
0224                 for (bufp = buf, wresid = rcount; ;
0225                     bufp += wcount, wresid -= wcount) {
0226                     wcount = write(to_fd, bufp, wresid);
0227                     if (wcount <= 0)
0228                         break;
0229                     wtotal += wcount;
0230                     if (info) {
0231                         info = 0;
0232                         (void)fprintf(stderr,
0233                             "%s -> %s %3d%%\n",
0234                             entp->fts_path, to.p_path,
0235                             cp_pct(wtotal, fs->st_size));
0236                     }
0237                     if (wcount >= (ssize_t)wresid)
0238                         break;
0239                 }
0240                 if (wcount != (ssize_t)wresid) {
0241                     warn("%s", to.p_path);
0242                     rval = 1;
0243                     break;
0244                 }
0245             }
0246             if (rcount < 0) {
0247                 warn("%s", entp->fts_path);
0248                 rval = 1;
0249             }
0250         }
0251     } else {
0252         if (link(entp->fts_path, to.p_path)) {
0253             warn("%s", to.p_path);
0254             rval = 1;
0255         }
0256     }
0257     (void)close(from_fd);
0258 
0259     /*
0260      * Don't remove the target even after an error.  The target might
0261      * not be a regular file, or its attributes might be important,
0262      * or its contents might be irreplaceable.  It would only be safe
0263      * to remove it if we created it and its length is 0.
0264      */
0265 
0266     if (!lflag) {
0267         if (pflag && setfile(cp_globals, fs, to_fd))
0268             rval = 1;
0269         if (pflag && preserve_fd_acls(from_fd, to_fd) != 0)
0270             rval = 1;
0271         (void)close(from_fd);
0272         if (close(to_fd)) {
0273             warn("%s", to.p_path);
0274             rval = 1;
0275         }
0276     }
0277   (void)free(buf);
0278     return (rval);
0279 }
0280 
0281 int
0282 copy_link(rtems_shell_cp_globals* cp_globals, FTSENT *p, int exists)
0283 {
0284     ssize_t len;
0285     char llink[PATH_MAX];
0286 
0287     if ((len = readlink(p->fts_path, llink, sizeof(llink) - 1)) == -1) {
0288         warn("readlink: %s", p->fts_path);
0289         return (1);
0290     }
0291     llink[len] = '\0';
0292     if (exists && unlink(to.p_path)) {
0293         warn("unlink: %s", to.p_path);
0294         return (1);
0295     }
0296     if (symlink(llink, to.p_path)) {
0297         warn("symlink: %s", llink);
0298         return (1);
0299     }
0300     return (pflag ? setfile(cp_globals, p->fts_statp, -1) : 0);
0301 }
0302 
0303 int
0304 copy_fifo(rtems_shell_cp_globals* cp_globals, struct stat *from_stat, int exists)
0305 {
0306     if (exists && unlink(to.p_path)) {
0307         warn("unlink: %s", to.p_path);
0308         return (1);
0309     }
0310     if (mkfifo(to.p_path, from_stat->st_mode)) {
0311         warn("mkfifo: %s", to.p_path);
0312         return (1);
0313     }
0314     return (pflag ? setfile(cp_globals, from_stat, -1) : 0);
0315 }
0316 
0317 int
0318 copy_special(rtems_shell_cp_globals* cp_globals, struct stat *from_stat, int exists)
0319 {
0320     if (exists && unlink(to.p_path)) {
0321         warn("unlink: %s", to.p_path);
0322         return (1);
0323     }
0324     if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) {
0325         warn("mknod: %s", to.p_path);
0326         return (1);
0327     }
0328     return (pflag ? setfile(cp_globals, from_stat, -1) : 0);
0329 }
0330 
0331 #if defined(__rtems__)
0332   /*
0333    * Newlib's <sys/timespec.h> has the real BSD definition of this macro
0334    * and it does not behave the same as this one. Thus we need to undefine
0335    * the BSD standard one and use the one expected by this file.
0336    */
0337   #undef TIMESPEC_TO_TIMEVAL
0338 #endif
0339 
0340 #define TIMESPEC_TO_TIMEVAL(tv, ts) {                                   \
0341         (tv)->tv_sec = *(ts);                                           \
0342         (tv)->tv_usec = 0;                                              \
0343 }
0344 
0345 #define st_atimespec st_atime
0346 #define st_mtimespec st_mtime
0347 #define lutimes utimes
0348 
0349 int
0350 setfile(rtems_shell_cp_globals* cp_globals, struct stat *fs, int fd)
0351 {
0352     static struct timeval tv[2];
0353     struct stat ts;
0354     int rval, gotstat, islink, fdval;
0355 
0356     rval = 0;
0357     fdval = fd != -1;
0358     islink = !fdval && S_ISLNK(fs->st_mode);
0359     fs->st_mode &= S_ISUID | S_ISGID | S_ISVTX |
0360                S_IRWXU | S_IRWXG | S_IRWXO;
0361 
0362     TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec);
0363     TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec);
0364 #if 0
0365     if (islink ? lutimes(to.p_path, tv) : utimes(to.p_path, tv)) {
0366         warn("%sutimes: %s", islink ? "l" : "", to.p_path);
0367         rval = 1;
0368     }
0369 #endif
0370     if (fdval ? fstat(fd, &ts) :
0371         (islink ? lstat(to.p_path, &ts) : stat(to.p_path, &ts)))
0372         gotstat = 0;
0373     else {
0374         gotstat = 1;
0375         ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX |
0376                   S_IRWXU | S_IRWXG | S_IRWXO;
0377     }
0378     /*
0379      * Changing the ownership probably won't succeed, unless we're root
0380      * or POSIX_CHOWN_RESTRICTED is not set.  Set uid/gid before setting
0381      * the mode; current BSD behavior is to remove all setuid bits on
0382      * chown.  If chown fails, lose setuid/setgid bits.
0383      */
0384     if (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid)
0385         if (fdval ? fchown(fd, fs->st_uid, fs->st_gid) :
0386             (islink ? lchown(to.p_path, fs->st_uid, fs->st_gid) :
0387             chown(to.p_path, fs->st_uid, fs->st_gid))) {
0388             if (errno != EPERM) {
0389                 warn("chown: %s", to.p_path);
0390                 rval = 1;
0391             }
0392             fs->st_mode &= ~(S_ISUID | S_ISGID);
0393         }
0394 
0395     if (!gotstat || fs->st_mode != ts.st_mode)
0396         if (fdval ? fchmod(fd, fs->st_mode) :
0397             (islink ? lchmod(to.p_path, fs->st_mode) :
0398             chmod(to.p_path, fs->st_mode))) {
0399             warn("chmod: %s", to.p_path);
0400             rval = 1;
0401         }
0402 
0403 #if 0
0404     if (!gotstat || fs->st_flags != ts.st_flags)
0405         if (fdval ?
0406             fchflags(fd, fs->st_flags) :
0407             (islink ? (errno = ENOSYS) :
0408             chflags(to.p_path, fs->st_flags))) {
0409             warn("chflags: %s", to.p_path);
0410             rval = 1;
0411         }
0412 #endif
0413 
0414     return (rval);
0415 }
0416 
0417 int
0418 preserve_fd_acls(int source_fd RTEMS_UNUSED, int dest_fd RTEMS_UNUSED)
0419 {
0420 #if 0
0421     struct acl *aclp;
0422     acl_t acl;
0423 
0424     if (fpathconf(source_fd, _PC_ACL_EXTENDED) != 1 ||
0425         fpathconf(dest_fd, _PC_ACL_EXTENDED) != 1)
0426         return (0);
0427     acl = acl_get_fd(source_fd);
0428     if (acl == NULL) {
0429         warn("failed to get acl entries while setting %s", to.p_path);
0430         return (1);
0431     }
0432     aclp = &acl->ats_acl;
0433     if (aclp->acl_cnt == 3)
0434         return (0);
0435     if (acl_set_fd(dest_fd, acl) < 0) {
0436         warn("failed to set acl entries for %s", to.p_path);
0437         return (1);
0438     }
0439 #endif
0440     return (0);
0441 }
0442 
0443 int
0444 preserve_dir_acls(struct stat *fs RTEMS_UNUSED, char *source_dir RTEMS_UNUSED, char *dest_dir RTEMS_UNUSED)
0445 {
0446 #if 0
0447     acl_t (*aclgetf)(const char *, acl_type_t);
0448     int (*aclsetf)(const char *, acl_type_t, acl_t);
0449     struct acl *aclp;
0450     acl_t acl;
0451 
0452     if (pathconf(source_dir, _PC_ACL_EXTENDED) != 1 ||
0453         pathconf(dest_dir, _PC_ACL_EXTENDED) != 1)
0454         return (0);
0455     /*
0456      * If the file is a link we will not follow it
0457      */
0458     if (S_ISLNK(fs->st_mode)) {
0459         aclgetf = acl_get_link_np;
0460         aclsetf = acl_set_link_np;
0461     } else {
0462         aclgetf = acl_get_file;
0463         aclsetf = acl_set_file;
0464     }
0465     /*
0466      * Even if there is no ACL_TYPE_DEFAULT entry here, a zero
0467      * size ACL will be returned. So it is not safe to simply
0468      * check the pointer to see if the default ACL is present.
0469      */
0470     acl = aclgetf(source_dir, ACL_TYPE_DEFAULT);
0471     if (acl == NULL) {
0472         warn("failed to get default acl entries on %s",
0473             source_dir);
0474         return (1);
0475     }
0476     aclp = &acl->ats_acl;
0477     if (aclp->acl_cnt != 0 && aclsetf(dest_dir,
0478         ACL_TYPE_DEFAULT, acl) < 0) {
0479         warn("failed to set default acl entries on %s",
0480             dest_dir);
0481         return (1);
0482     }
0483     acl = aclgetf(source_dir, ACL_TYPE_ACCESS);
0484     if (acl == NULL) {
0485         warn("failed to get acl entries on %s", source_dir);
0486         return (1);
0487     }
0488     aclp = &acl->ats_acl;
0489     if (aclsetf(dest_dir, ACL_TYPE_ACCESS, acl) < 0) {
0490         warn("failed to set acl entries on %s", dest_dir);
0491         return (1);
0492     }
0493 #endif
0494     return (0);
0495 }
0496 
0497 void
0498 usage(rtems_shell_cp_globals* cp_globals)
0499 {
0500     (void)fprintf(stderr, "%s\n%s\n",
0501 "usage: cp [-R [-H | -L | -P]] [-f | -i | -n] [-alpv] source_file target_file",
0502 "       cp [-R [-H | -L | -P]] [-f | -i | -n] [-alpv] source_file ... "
0503 "target_directory");
0504   longjmp (cp_globals->exit_jmp, 1);
0505 }