/* * This function adds a journal inode to a filesystem, using either * POSIX routines if the filesystem is mounted, or using direct I/O * functions if it is not. */ errcode_t ext2fs_add_journal_inode2(ext2_filsys fs, blk_t num_blocks, blk64_t goal, int flags) { errcode_t retval; ext2_ino_t journal_ino; struct stat st; char jfile[1024]; int mount_flags; int fd = -1; if (flags & EXT2_MKJOURNAL_NO_MNT_CHECK) mount_flags = 0; else if ((retval = ext2fs_check_mount_point(fs->device_name, &mount_flags, jfile, sizeof(jfile)-10))) return retval; if (mount_flags & EXT2_MF_MOUNTED) { #if HAVE_EXT2_IOCTLS int f = 0; #endif strcat(jfile, "/.journal"); /* * If .../.journal already exists, make sure any * immutable or append-only flags are cleared. */ #if defined(HAVE_CHFLAGS) && defined(UF_NODUMP) (void) chflags (jfile, 0); #else #if HAVE_EXT2_IOCTLS fd = open(jfile, O_RDONLY); if (fd >= 0) { retval = ioctl(fd, EXT2_IOC_SETFLAGS, &f); close(fd); if (retval) return retval; } #endif #endif /* Create the journal file */ if ((fd = open(jfile, O_CREAT|O_WRONLY, 0600)) < 0) return errno; /* Note that we can't do lazy journal initialization for mounted * filesystems, since the zero writing is also allocating the * journal blocks. We could use fallocate, but not all kernels * support that, and creating a journal on a mounted ext2 * filesystems is extremely rare these days... Ignore it. */ flags &= ~EXT2_MKJOURNAL_LAZYINIT; if ((retval = write_journal_file(fs, jfile, num_blocks, flags))) goto errout; /* Get inode number of the journal file */ if (fstat(fd, &st) < 0) { retval = errno; goto errout; } #if defined(HAVE_CHFLAGS) && defined(UF_NODUMP) retval = fchflags (fd, UF_NODUMP|UF_IMMUTABLE); #else #if HAVE_EXT2_IOCTLS if (ioctl(fd, EXT2_IOC_GETFLAGS, &f) < 0) { retval = errno; goto errout; } f |= EXT2_NODUMP_FL | EXT2_IMMUTABLE_FL; retval = ioctl(fd, EXT2_IOC_SETFLAGS, &f); #endif #endif if (retval) { retval = errno; goto errout; } if (close(fd) < 0) { retval = errno; fd = -1; goto errout; } journal_ino = st.st_ino; } else { if ((mount_flags & EXT2_MF_BUSY) && !(fs->flags & EXT2_FLAG_EXCLUSIVE)) { retval = EBUSY; goto errout; } journal_ino = EXT2_JOURNAL_INO; if ((retval = write_journal_inode(fs, journal_ino, num_blocks, goal, flags))) return retval; } fs->super->s_journal_inum = journal_ino; fs->super->s_journal_dev = 0; memset(fs->super->s_journal_uuid, 0, sizeof(fs->super->s_journal_uuid)); fs->super->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL; ext2fs_mark_super_dirty(fs); return 0; errout: if (fd >= 0) close(fd); return retval; }
/* * Function: setfile * * Purpose: * Set the owner/group/permissions for the "to" file to the information * in the stat structure. If fd is zero, also call set_utimes() to set * the mod/access times. If fd is non-zero, the caller must do a utimes * itself after close(fd). */ int setfile(struct stat *fs, int fd) { int rval = 0; #ifdef BSD int islink = S_ISLNK(fs->st_mode); #endif fs->st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO; /* * Changing the ownership probably won't succeed, unless we're root * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting * the mode; current BSD behavior is to remove all setuid bits on * chown. If chown fails, lose setuid/setgid bits. */ if (fd ? fchown(fd, fs->st_uid, fs->st_gid) : lchown(to.p_path, fs->st_uid, fs->st_gid)) { if (errno != EPERM) { warn("chown: %s", to.p_path); rval = 1; } fs->st_mode &= ~(S_ISUID | S_ISGID); } #ifndef BSD if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) { #else if (fd ? fchmod(fd, fs->st_mode) : lchmod(to.p_path, fs->st_mode)) { #endif warn("chmod: %s", to.p_path); rval = 1; } #ifdef BSD if (!islink && !Nflag) { unsigned long fflags = fs->st_flags; /* * XXX * NFS doesn't support chflags; ignore errors unless * there's reason to believe we're losing bits. * (Note, this still won't be right if the server * supports flags and we were trying to *remove* flags * on a file that we copied, i.e., that we didn't create.) */ errno = 0; if ((fd ? fchflags(fd, fflags) : chflags(to.p_path, fflags)) == -1) if (errno != EOPNOTSUPP || fs->st_flags != 0) { warn("chflags: %s", to.p_path); rval = 1; } } #endif /* if fd is non-zero, caller must call set_utimes() after close() */ if (fd == 0 && set_utimes(to.p_path, fs)) rval = 1; return (rval); } void cp_usage(void) { (void)fprintf(stderr, "usage: cp [-R [-H | -L | -P]] [-f | -i] [-alNpv] src target\n" " cp [-R [-H | -L | -P]] [-f | -i] [-alNpv] src1 ... srcN directory\n"); exit(1); /* NOTREACHED */ }
/* * This function adds a journal inode to a filesystem, using either * POSIX routines if the filesystem is mounted, or using direct I/O * functions if it is not. */ errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t size, int flags) { errcode_t retval; ext2_ino_t journal_ino; struct stat st; char jfile[1024]; int mount_flags, f; int fd = -1; if ((retval = ext2fs_check_mount_point(fs->device_name, &mount_flags, jfile, sizeof(jfile)-10))) return retval; if (mount_flags & EXT2_MF_MOUNTED) { strcat(jfile, "/.journal"); /* * If .../.journal already exists, make sure any * immutable or append-only flags are cleared. */ #if defined(HAVE_CHFLAGS) && defined(UF_NODUMP) (void) chflags (jfile, 0); #else #if HAVE_EXT2_IOCTLS fd = open(jfile, O_RDONLY); if (fd >= 0) { f = 0; ioctl(fd, EXT2_IOC_SETFLAGS, &f); close(fd); } #endif #endif /* Create the journal file */ if ((fd = open(jfile, O_CREAT|O_WRONLY, 0600)) < 0) return errno; if ((retval = write_journal_file(fs, jfile, size, flags))) goto errout; /* Get inode number of the journal file */ if (fstat(fd, &st) < 0) goto errout; #if defined(HAVE_CHFLAGS) && defined(UF_NODUMP) retval = fchflags (fd, UF_NODUMP|UF_IMMUTABLE); #else #if HAVE_EXT2_IOCTLS f = EXT2_NODUMP_FL | EXT2_IMMUTABLE_FL; retval = ioctl(fd, EXT2_IOC_SETFLAGS, &f); #endif #endif if (retval) goto errout; close(fd); journal_ino = st.st_ino; } else { if ((mount_flags & EXT2_MF_BUSY) && !(fs->flags & EXT2_FLAG_EXCLUSIVE)) { retval = EBUSY; goto errout; } journal_ino = EXT2_JOURNAL_INO; if ((retval = write_journal_inode(fs, journal_ino, size, flags))) return retval; } fs->super->s_journal_inum = journal_ino; fs->super->s_journal_dev = 0; memset(fs->super->s_journal_uuid, 0, sizeof(fs->super->s_journal_uuid)); fs->super->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL; ext2fs_mark_super_dirty(fs); return 0; errout: if (fd > 0) close(fd); return retval; }
static int fastcopy(const char *from, const char *to, struct stat *sbp) { struct timeval tval[2]; static u_int blen = MAXPHYS; static char *bp = NULL; mode_t oldmode; int nread, from_fd, to_fd; if ((from_fd = open(from, O_RDONLY, 0)) < 0) { warn("fastcopy: open() failed (from): %s", from); return (1); } if (bp == NULL && (bp = malloc((size_t)blen)) == NULL) { warnx("malloc(%u) failed", blen); return (1); } while ((to_fd = open(to, O_CREAT | O_EXCL | O_TRUNC | O_WRONLY, 0)) < 0) { if (errno == EEXIST && unlink(to) == 0) continue; warn("fastcopy: open() failed (to): %s", to); (void)close(from_fd); return (1); } while ((nread = read(from_fd, bp, (size_t)blen)) > 0) if (write(to_fd, bp, (size_t)nread) != nread) { warn("fastcopy: write() failed: %s", to); goto err; } if (nread < 0) { warn("fastcopy: read() failed: %s", from); err: if (unlink(to)) warn("%s: remove", to); (void)close(from_fd); (void)close(to_fd); return (1); } oldmode = sbp->st_mode & ALLPERMS; if (fchown(to_fd, sbp->st_uid, sbp->st_gid)) { warn("%s: set owner/group (was: %lu/%lu)", to, (u_long)sbp->st_uid, (u_long)sbp->st_gid); if (oldmode & (S_ISUID | S_ISGID)) { warnx( "%s: owner/group changed; clearing suid/sgid (mode was 0%03o)", to, oldmode); sbp->st_mode &= ~(S_ISUID | S_ISGID); } } if (fchmod(to_fd, sbp->st_mode)) warn("%s: set mode (was: 0%03o)", to, oldmode); /* * POSIX 1003.2c states that if _POSIX_ACL_EXTENDED is in effect * for dest_file, then its ACLs shall reflect the ACLs of the * source_file. */ preserve_fd_acls(from_fd, to_fd, from, to); (void)close(from_fd); /* * XXX * NFS doesn't support chflags; ignore errors unless there's reason * to believe we're losing bits. (Note, this still won't be right * if the server supports flags and we were trying to *remove* flags * on a file that we copied, i.e., that we didn't create.) */ errno = 0; if (fchflags(to_fd, (u_long)sbp->st_flags)) if (errno != EOPNOTSUPP || sbp->st_flags != 0) warn("%s: set flags (was: 0%07o)", to, sbp->st_flags); tval[0].tv_sec = sbp->st_atime; tval[1].tv_sec = sbp->st_mtime; tval[0].tv_usec = tval[1].tv_usec = 0; if (utimes(to, tval)) warn("%s: set times", to); if (close(to_fd)) { warn("%s", to); return (1); } if (unlink(from)) { warn("%s: remove", from); return (1); } if (vflg) printf("%s -> %s\n", from, to); return (0); }
int setfile(struct stat *fs, int fd) { static struct timeval tv[2]; struct stat ts; int rval, gotstat, islink, fdval; rval = 0; fdval = fd != -1; islink = !fdval && S_ISLNK(fs->st_mode); fs->st_mode &= S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO; TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atim); TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtim); if (islink ? lutimes(to.p_path, tv) : utimes(to.p_path, tv)) { warn("%sutimes: %s", islink ? "l" : "", to.p_path); rval = 1; } if (fdval ? fstat(fd, &ts) : (islink ? lstat(to.p_path, &ts) : stat(to.p_path, &ts))) gotstat = 0; else { gotstat = 1; ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO; } /* * Changing the ownership probably won't succeed, unless we're root * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting * the mode; current BSD behavior is to remove all setuid bits on * chown. If chown fails, lose setuid/setgid bits. */ if (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid) if (fdval ? fchown(fd, fs->st_uid, fs->st_gid) : (islink ? lchown(to.p_path, fs->st_uid, fs->st_gid) : chown(to.p_path, fs->st_uid, fs->st_gid))) { if (errno != EPERM) { warn("chown: %s", to.p_path); rval = 1; } fs->st_mode &= ~(S_ISUID | S_ISGID); } if (!gotstat || fs->st_mode != ts.st_mode) if (fdval ? fchmod(fd, fs->st_mode) : (islink ? lchmod(to.p_path, fs->st_mode) : chmod(to.p_path, fs->st_mode))) { warn("chmod: %s", to.p_path); rval = 1; } if (!gotstat || fs->st_flags != ts.st_flags) if (fdval ? fchflags(fd, fs->st_flags) : (islink ? lchflags(to.p_path, fs->st_flags) : chflags(to.p_path, fs->st_flags))) { warn("chflags: %s", to.p_path); rval = 1; } return (rval); }
int extractfile(char *name) { int flags; uid_t uid; gid_t gid; mode_t mode; int extsize; struct timeval mtimep[2], ctimep[2]; struct entry *ep; char *buf; curfile.name = name; curfile.action = USING; mtimep[0].tv_sec = curfile.atime_sec; mtimep[0].tv_usec = curfile.atime_nsec / 1000; mtimep[1].tv_sec = curfile.mtime_sec; mtimep[1].tv_usec = curfile.mtime_nsec / 1000; ctimep[0].tv_sec = curfile.atime_sec; ctimep[0].tv_usec = curfile.atime_nsec / 1000; ctimep[1].tv_sec = curfile.birthtime_sec; ctimep[1].tv_usec = curfile.birthtime_nsec / 1000; extsize = curfile.extsize; uid = getuid(); if (uid == 0) uid = curfile.uid; gid = curfile.gid; mode = curfile.mode; flags = curfile.file_flags; switch (mode & IFMT) { default: fprintf(stderr, "%s: unknown file mode 0%o\n", name, mode); skipfile(); return (FAIL); case IFSOCK: vprintf(stdout, "skipped socket %s\n", name); skipfile(); return (GOOD); case IFDIR: if (mflag) { ep = lookupname(name); if (ep == NULL || ep->e_flags & EXTRACT) panic("unextracted directory %s\n", name); skipfile(); return (GOOD); } vprintf(stdout, "extract file %s\n", name); return (genliteraldir(name, curfile.ino)); case IFLNK: lnkbuf[0] = '\0'; pathlen = 0; buf = setupextattr(extsize); getfile(xtrlnkfile, xtrattr, xtrlnkskip); if (pathlen == 0) { vprintf(stdout, "%s: zero length symbolic link (ignored)\n", name); return (GOOD); } if (linkit(lnkbuf, name, SYMLINK) == GOOD) { if (extsize > 0) set_extattr_link(name, buf, extsize); (void) lchown(name, uid, gid); (void) lchmod(name, mode); (void) lutimes(name, ctimep); (void) lutimes(name, mtimep); (void) lchflags(name, flags); return (GOOD); } return (FAIL); case IFIFO: vprintf(stdout, "extract fifo %s\n", name); if (Nflag) { skipfile(); return (GOOD); } if (uflag) (void) unlink(name); if (mkfifo(name, 0600) < 0) { fprintf(stderr, "%s: cannot create fifo: %s\n", name, strerror(errno)); skipfile(); return (FAIL); } if (extsize == 0) { skipfile(); } else { buf = setupextattr(extsize); getfile(xtrnull, xtrattr, xtrnull); set_extattr_file(name, buf, extsize); } (void) chown(name, uid, gid); (void) chmod(name, mode); (void) utimes(name, ctimep); (void) utimes(name, mtimep); (void) chflags(name, flags); return (GOOD); case IFCHR: case IFBLK: vprintf(stdout, "extract special file %s\n", name); if (Nflag) { skipfile(); return (GOOD); } if (uflag) (void) unlink(name); if (mknod(name, (mode & (IFCHR | IFBLK)) | 0600, (int)curfile.rdev) < 0) { fprintf(stderr, "%s: cannot create special file: %s\n", name, strerror(errno)); skipfile(); return (FAIL); } if (extsize == 0) { skipfile(); } else { buf = setupextattr(extsize); getfile(xtrnull, xtrattr, xtrnull); set_extattr_file(name, buf, extsize); } (void) chown(name, uid, gid); (void) chmod(name, mode); (void) utimes(name, ctimep); (void) utimes(name, mtimep); (void) chflags(name, flags); return (GOOD); case IFREG: vprintf(stdout, "extract file %s\n", name); if (Nflag) { skipfile(); return (GOOD); } if (uflag) (void) unlink(name); if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0) { fprintf(stderr, "%s: cannot create file: %s\n", name, strerror(errno)); skipfile(); return (FAIL); } buf = setupextattr(extsize); getfile(xtrfile, xtrattr, xtrskip); if (extsize > 0) set_extattr_fd(ofile, name, buf, extsize); (void) fchown(ofile, uid, gid); (void) fchmod(ofile, mode); (void) futimes(ofile, ctimep); (void) futimes(ofile, mtimep); (void) fchflags(ofile, flags); (void) close(ofile); return (GOOD); } /* NOTREACHED */ }
void copymkdir(int rootfd, char const * dir, int skelfd, mode_t mode, uid_t uid, gid_t gid, int flags) { char *p, lnk[MAXPATHLEN], copybuf[4096]; int len, homefd, srcfd, destfd; ssize_t sz; struct stat st; struct dirent *e; DIR *d; if (*dir == '/') dir++; if (mkdirat(rootfd, dir, mode) != 0 && errno != EEXIST) { warn("mkdir(%s)", dir); return; } fchownat(rootfd, dir, uid, gid, AT_SYMLINK_NOFOLLOW); if (flags > 0) chflagsat(rootfd, dir, flags, AT_SYMLINK_NOFOLLOW); if (skelfd == -1) return; homefd = openat(rootfd, dir, O_DIRECTORY); if ((d = fdopendir(skelfd)) == NULL) { close(skelfd); close(homefd); return; } while ((e = readdir(d)) != NULL) { if (strcmp(e->d_name, ".") == 0 || strcmp(e->d_name, "..") == 0) continue; p = e->d_name; if (fstatat(skelfd, p, &st, AT_SYMLINK_NOFOLLOW) == -1) continue; if (strncmp(p, "dot.", 4) == 0) /* Conversion */ p += 3; if (S_ISDIR(st.st_mode)) { copymkdir(homefd, p, openat(skelfd, e->d_name, O_DIRECTORY), st.st_mode & _DEF_DIRMODE, uid, gid, st.st_flags); continue; } if (S_ISLNK(st.st_mode) && (len = readlinkat(skelfd, e->d_name, lnk, sizeof(lnk) -1)) != -1) { lnk[len] = '\0'; symlinkat(lnk, homefd, p); fchownat(homefd, p, uid, gid, AT_SYMLINK_NOFOLLOW); continue; } if (!S_ISREG(st.st_mode)) continue; if ((srcfd = openat(skelfd, e->d_name, O_RDONLY)) == -1) continue; destfd = openat(homefd, p, O_RDWR | O_CREAT | O_EXCL, st.st_mode); if (destfd == -1) { close(srcfd); continue; } while ((sz = read(srcfd, copybuf, sizeof(copybuf))) > 0) write(destfd, copybuf, sz); close(srcfd); /* * Propagate special filesystem flags */ fchown(destfd, uid, gid); fchflags(destfd, st.st_flags); close(destfd); } closedir(d); }
static int handle_request_thread(void *arg) { #pragma unused(arg) int error; webdav_requestqueue_element_t * myrequest; struct timespec timeout; int idleRecheck = 0; while (TRUE) { error = pthread_mutex_lock(&requests_lock); require_noerr(error, pthread_mutex_lock); /* Check to see if there is a request to process */ if (waiting_requests.request_count > 0) { /* There is a request so dequeue it */ idleRecheck = 0; /* reset this flag to indicate that we did find work to do */ myrequest = waiting_requests.item_head; --(waiting_requests.request_count); if (waiting_requests.request_count > 0) { /* There was more than one item on */ /* the queue so bump the pointers */ waiting_requests.item_head = myrequest->next; } else { waiting_requests.item_head = waiting_requests.item_tail = 0; } /* Ok, now unlock the queue and go about handling the request */ error = pthread_mutex_unlock(&requests_lock); require_noerr(error, pthread_mutex_unlock); switch (myrequest->type) { case WEBDAV_REQUEST_TYPE: handle_filesystem_request(myrequest->element.request.socket); break; case WEBDAV_DOWNLOAD_TYPE: /* finish the download */ error = network_finish_download(myrequest->element.download.node, myrequest->element.download.readStreamRecPtr); if (error) { /* Set append to indicate that our download failed. It's a hack, but * it should work. Be sure to still mark the download as finished so * that if we were terminated early the closer will be notified */ verify_noerr(fchflags(myrequest->element.download.node->file_fd, UF_APPEND)); myrequest->element.download.node->file_status = WEBDAV_DOWNLOAD_ABORTED; } else { /* Clear flags to indicate that our download is complete. It's a hack, but * it should work. Be sure to still mark the download as finished so * that if we were terminated early the closer will be notified */ verify_noerr(fchflags(myrequest->element.download.node->file_fd, 0)); myrequest->element.download.node->file_status = WEBDAV_DOWNLOAD_FINISHED; } error = 0; break; case WEBDAV_SERVER_PING_TYPE: /* Send an OPTIONS request to the server. */ network_server_ping(myrequest->element.serverping.delay); break; case WEBDAV_SEQWRITE_MANAGER_TYPE: /* Read the response stream of a sequential write sequence */ network_seqwrite_manager(myrequest->element.seqwrite_read_rsp.ctx); break; default: /* nothing we can do, just get the next request */ break; } free(myrequest); } else { /* There were no requests to handle. If idleRecheck is set, then we just timed out waiting for work and we did one more paranoid check for work and still found no work, so its time to exit the thread. If idleRecheck is not set, then there is no work to do right now. Wait on the condition variable and also have a max timeout to wait. If we timeout and there was no work to do, then set idleRecheck flag and loop around one more time to look for work, just to be paranoid. The extra loop around with idleRecheck is for the case where a thread is waiting on condition variable, and it times out. It then gets blocked waiting to acquire the request_lock while a request is being put on the work queue (and thus the idle thread count is greater than 0). It then gets the request_lock and there is not work on the queue for it to do, but the time out has also expired. To be sure that the work gets picked up, we do the extra loop around to check for work. */ if (idleRecheck == 1) { /* still no work to do after timing out and rechecking again for work, so exit thread */ //LogMessage (kSysLog, "handle_request_thread - thread %d exiting since no work to do\n", pthread_self()); gCurrThreadCount -= 1; error = pthread_mutex_unlock(&requests_lock); pthread_exit(NULL); } timeout.tv_sec = time(NULL) + WEBDAV_MAX_IDLE_TIME; /* time out in seconds */ timeout.tv_nsec = 0; //LogMessage (kSysLog, "handle_request_thread - thread %d idle and waiting for work\n", pthread_self()); gIdleThreadCount += 1; /* increment to indicate one idle thread */ error = pthread_cond_timedwait(&requests_condvar, &requests_lock, &timeout); gIdleThreadCount -= 1; /* decrement number of idle threads */ require((error == ETIMEDOUT || error == 0), pthread_cond_wait); if (error == ETIMEDOUT) { /* time out has occurred and still no work to do. Loop around one more time just to be paranoid that there is no work to do */ //LogMessage (kSysLog, "handle_request_thread - thread %d timeout, doing one more check\n", pthread_self()); idleRecheck = 1; } /* Unlock so that we can restart the loop */ error = pthread_mutex_unlock(&requests_lock); require_noerr(error, pthread_mutex_unlock); } } pthread_cond_wait: pthread_mutex_unlock: pthread_mutex_lock: /* errors coming out of this routine are fatal */ if ( error ) { webdav_kill(-1); /* tell the main select loop to force unmount */ } return error; }
/* * install -- * build a path name and install the file */ void install(const char *from_name, const char *to_name, u_long fset, u_long fclr, u_int flags) { struct stat from_sb, temp_sb, to_sb; struct utimbuf utb; int devnull, files_match, from_fd, serrno, target; int tempcopy, temp_fd, to_fd; u_long nfset; char backup[MAXPATHLEN], *p, pathbuf[MAXPATHLEN], tempfile[MAXPATHLEN]; files_match = 0; from_fd = -1; /* If try to install NULL file to a directory, fails. */ if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) { if (stat(from_name, &from_sb)) err(EX_OSERR, "%s", from_name); if (!S_ISREG(from_sb.st_mode)) { errno = EFTYPE; err(EX_OSERR, "%s", from_name); } /* Build the target path. */ if (flags & DIRECTORY) { snprintf(pathbuf, sizeof(pathbuf), "%s/%s", to_name, (p = strrchr(from_name, '/')) ? ++p : from_name); to_name = pathbuf; } devnull = 0; } else { devnull = 1; } target = stat(to_name, &to_sb) == 0; /* Only install to regular files. */ if (target && !S_ISREG(to_sb.st_mode)) { errno = EFTYPE; warn("%s", to_name); return; } /* Only copy safe if the target exists. */ tempcopy = safecopy && target; if (!devnull && (from_fd = open(from_name, O_RDONLY, 0)) < 0) err(EX_OSERR, "%s", from_name); /* If we don't strip, we can compare first. */ if (docompare && !dostrip && target) { if ((to_fd = open(to_name, O_RDONLY, 0)) < 0) err(EX_OSERR, "%s", to_name); if (devnull) files_match = to_sb.st_size == 0; else files_match = !(compare(from_fd, from_name, (size_t)from_sb.st_size, to_fd, to_name, (size_t)to_sb.st_size)); /* Close "to" file unless we match. */ if (!files_match) close(to_fd); } if (!files_match) { if (tempcopy) { to_fd = create_tempfile(to_name, tempfile, sizeof(tempfile)); if (to_fd < 0) err(EX_OSERR, "%s", tempfile); } else { if ((to_fd = create_newfile(to_name, target, &to_sb)) < 0) err(EX_OSERR, "%s", to_name); if (verbose) printf("install: %s -> %s\n", from_name, to_name); } if (!devnull) copy(from_fd, from_name, to_fd, tempcopy ? tempfile : to_name, from_sb.st_size); } if (dostrip) { strip(tempcopy ? tempfile : to_name); /* * Re-open our fd on the target, in case we used a strip * that does not work in-place -- like GNU binutils strip. */ close(to_fd); to_fd = open(tempcopy ? tempfile : to_name, O_RDONLY, 0); if (to_fd < 0) err(EX_OSERR, "stripping %s", to_name); } /* * Compare the stripped temp file with the target. */ if (docompare && dostrip && target) { temp_fd = to_fd; /* Re-open to_fd using the real target name. */ if ((to_fd = open(to_name, O_RDONLY, 0)) < 0) err(EX_OSERR, "%s", to_name); if (fstat(temp_fd, &temp_sb)) { serrno = errno; unlink(tempfile); errno = serrno; err(EX_OSERR, "%s", tempfile); } if (compare(temp_fd, tempfile, (size_t)temp_sb.st_size, to_fd, to_name, (size_t)to_sb.st_size) == 0) { /* * If target has more than one link we need to * replace it in order to snap the extra links. * Need to preserve target file times, though. */ if (to_sb.st_nlink != 1) { utb.actime = to_sb.st_atime; utb.modtime = to_sb.st_mtime; utime(tempfile, &utb); } else { files_match = 1; unlink(tempfile); } close(temp_fd); } } /* * Move the new file into place if doing a safe copy * and the files are different (or just not compared). */ if (tempcopy && !files_match) { /* Try to turn off the immutable bits. */ if (to_sb.st_flags & NOCHANGEBITS) chflags(to_name, to_sb.st_flags & ~NOCHANGEBITS); if (dobackup) { if ((size_t)snprintf(backup, MAXPATHLEN, "%s%s", to_name, suffix) != strlen(to_name) + strlen(suffix)) { unlink(tempfile); errx(EX_OSERR, "%s: backup filename too long", to_name); } if (verbose) printf("install: %s -> %s\n", to_name, backup); if (rename(to_name, backup) < 0) { serrno = errno; unlink(tempfile); errno = serrno; err(EX_OSERR, "rename: %s to %s", to_name, backup); } } if (verbose) printf("install: %s -> %s\n", from_name, to_name); if (rename(tempfile, to_name) < 0) { serrno = errno; unlink(tempfile); errno = serrno; err(EX_OSERR, "rename: %s to %s", tempfile, to_name); } /* Re-open to_fd so we aren't hosed by the rename(2). */ close(to_fd); if ((to_fd = open(to_name, O_RDONLY, 0)) < 0) err(EX_OSERR, "%s", to_name); } /* * Preserve the timestamp of the source file if necessary. */ if (dopreserve && !files_match && !devnull) { utb.actime = from_sb.st_atime; utb.modtime = from_sb.st_mtime; utime(to_name, &utb); } if (fstat(to_fd, &to_sb) == -1) { serrno = errno; unlink(to_name); errno = serrno; err(EX_OSERR, "%s", to_name); } /* * Set owner, group, mode for target; do the chown first, * chown may lose the setuid bits. */ if ((gid != (gid_t)-1 && gid != to_sb.st_gid) || (uid != (uid_t)-1 && uid != to_sb.st_uid) || (mode != to_sb.st_mode)) { /* Try to turn off the immutable bits. */ if (to_sb.st_flags & NOCHANGEBITS) fchflags(to_fd, to_sb.st_flags & ~NOCHANGEBITS); } if ((gid != (gid_t)-1 && gid != to_sb.st_gid) || (uid != (uid_t)-1 && uid != to_sb.st_uid)) if (fchown(to_fd, uid, gid) == -1) { serrno = errno; unlink(to_name); errno = serrno; err(EX_OSERR,"%s: chown/chgrp", to_name); } if (mode != to_sb.st_mode) if (fchmod(to_fd, mode)) { serrno = errno; unlink(to_name); errno = serrno; err(EX_OSERR, "%s: chmod", to_name); } /* * If provided a set of flags, set them, otherwise, preserve the * flags, except for the dump and history flags. The dump flag * is left clear on the target while the history flag from when * the target was created (which is inherited from the target's * parent directory) is retained. */ if (flags & SETFLAGS) { nfset = (to_sb.st_flags | fset) & ~fclr; } else { nfset = (from_sb.st_flags & ~(UF_NODUMP | UF_NOHISTORY)) | (to_sb.st_flags & UF_NOHISTORY); } /* * NFS does not support flags. Ignore EOPNOTSUPP flags if we're just * trying to turn off UF_NODUMP. If we're trying to set real flags, * then warn if the the fs doesn't support it, otherwise fail. */ if (!devnull && fchflags(to_fd, nfset)) { if (flags & SETFLAGS) { if (errno == EOPNOTSUPP) warn("%s: chflags", to_name); else { serrno = errno; unlink(to_name); errno = serrno; err(EX_OSERR, "%s: chflags", to_name); } } } close(to_fd); if (!devnull) close(from_fd); }
void copymkdir(char const * dir, char const * skel, mode_t mode, uid_t uid, gid_t gid) { char src[MAXPATHLEN]; char dst[MAXPATHLEN]; char lnk[MAXPATHLEN]; int len; if (mkdir(dir, mode) != 0 && errno != EEXIST) { warn("mkdir(%s)", dir); } else { int infd, outfd; struct stat st; static char counter = 0; static char *copybuf = NULL; ++counter; chown(dir, uid, gid); if (skel != NULL && *skel != '\0') { DIR *d = opendir(skel); if (d != NULL) { struct dirent *e; while ((e = readdir(d)) != NULL) { char *p = e->d_name; if (snprintf(src, sizeof(src), "%s/%s", skel, p) >= (int)sizeof(src)) warn("warning: pathname too long '%s/%s' (skel not copied)", skel, p); else if (lstat(src, &st) == 0) { if (strncmp(p, "dot.", 4) == 0) /* Conversion */ p += 3; if (snprintf(dst, sizeof(dst), "%s/%s", dir, p) >= (int)sizeof(dst)) warn("warning: path too long '%s/%s' (skel file skipped)", dir, p); else { if (S_ISDIR(st.st_mode)) { /* Recurse for this */ if (strcmp(e->d_name, ".") != 0 && strcmp(e->d_name, "..") != 0) copymkdir(dst, src, st.st_mode & _DEF_DIRMODE, uid, gid); chflags(dst, st.st_flags); /* propagate flags */ } else if (S_ISLNK(st.st_mode) && (len = readlink(src, lnk, sizeof(lnk) - 1)) != -1) { lnk[len] = '\0'; symlink(lnk, dst); lchown(dst, uid, gid); /* * Note: don't propagate special attributes * but do propagate file flags */ } else if (S_ISREG(st.st_mode) && (outfd = open(dst, O_RDWR | O_CREAT | O_EXCL, st.st_mode)) != -1) { if ((infd = open(src, O_RDONLY)) == -1) { close(outfd); remove(dst); } else { int b; /* * Allocate our copy buffer if we need to */ if (copybuf == NULL) copybuf = malloc(4096); while ((b = read(infd, copybuf, 4096)) > 0) write(outfd, copybuf, b); close(infd); /* * Propagate special filesystem flags */ fchown(outfd, uid, gid); fchflags(outfd, st.st_flags); close(outfd); chown(dst, uid, gid); } } } } } closedir(d); } } if (--counter == 0 && copybuf != NULL) { free(copybuf); copybuf = NULL; } } }
/* * Given a file descriptor, create a capability with specific rights and * make sure only those rights work. */ static int try_file_ops(int fd, cap_rights_t rights) { struct stat sb; struct statfs sf; int fd_cap, fd_capcap; ssize_t ssize, ssize2; off_t off; void *p; char ch; int ret, is_nfs; struct pollfd pollfd; int success = PASSED; REQUIRE(fstatfs(fd, &sf)); is_nfs = (strncmp("nfs", sf.f_fstypename, sizeof(sf.f_fstypename)) == 0); REQUIRE(fd_cap = cap_new(fd, rights)); REQUIRE(fd_capcap = cap_new(fd_cap, rights)); CHECK(fd_capcap != fd_cap); pollfd.fd = fd_cap; pollfd.events = POLLIN | POLLERR | POLLHUP; pollfd.revents = 0; ssize = read(fd_cap, &ch, sizeof(ch)); CHECK_RESULT(read, CAP_READ | CAP_SEEK, ssize >= 0); ssize = pread(fd_cap, &ch, sizeof(ch), 0); ssize2 = pread(fd_cap, &ch, sizeof(ch), 0); CHECK_RESULT(pread, CAP_READ, ssize >= 0); CHECK(ssize == ssize2); ssize = write(fd_cap, &ch, sizeof(ch)); CHECK_RESULT(write, CAP_WRITE | CAP_SEEK, ssize >= 0); ssize = pwrite(fd_cap, &ch, sizeof(ch), 0); CHECK_RESULT(pwrite, CAP_WRITE, ssize >= 0); off = lseek(fd_cap, 0, SEEK_SET); CHECK_RESULT(lseek, CAP_SEEK, off >= 0); /* * Note: this is not expected to work over NFS. */ ret = fchflags(fd_cap, UF_NODUMP); CHECK_RESULT(fchflags, CAP_FCHFLAGS, (ret == 0) || (is_nfs && (errno == EOPNOTSUPP))); ret = fstat(fd_cap, &sb); CHECK_RESULT(fstat, CAP_FSTAT, ret == 0); p = mmap(NULL, getpagesize(), PROT_READ, MAP_SHARED, fd_cap, 0); CHECK_MMAP_RESULT(CAP_MMAP | CAP_READ); p = mmap(NULL, getpagesize(), PROT_WRITE, MAP_SHARED, fd_cap, 0); CHECK_MMAP_RESULT(CAP_MMAP | CAP_WRITE); p = mmap(NULL, getpagesize(), PROT_EXEC, MAP_SHARED, fd_cap, 0); CHECK_MMAP_RESULT(CAP_MMAP | CAP_MAPEXEC); p = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd_cap, 0); CHECK_MMAP_RESULT(CAP_MMAP | CAP_READ | CAP_WRITE); p = mmap(NULL, getpagesize(), PROT_READ | PROT_EXEC, MAP_SHARED, fd_cap, 0); CHECK_MMAP_RESULT(CAP_MMAP | CAP_READ | CAP_MAPEXEC); p = mmap(NULL, getpagesize(), PROT_EXEC | PROT_WRITE, MAP_SHARED, fd_cap, 0); CHECK_MMAP_RESULT(CAP_MMAP | CAP_MAPEXEC | CAP_WRITE); p = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, fd_cap, 0); CHECK_MMAP_RESULT(CAP_MMAP | CAP_READ | CAP_WRITE | CAP_MAPEXEC); ret = fsync(fd_cap); CHECK_RESULT(fsync, CAP_FSYNC, ret == 0); ret = fchown(fd_cap, -1, -1); CHECK_RESULT(fchown, CAP_FCHOWN, ret == 0); ret = fchmod(fd_cap, 0644); CHECK_RESULT(fchmod, CAP_FCHMOD, ret == 0); /* XXX flock */ ret = ftruncate(fd_cap, 0); CHECK_RESULT(ftruncate, CAP_FTRUNCATE, ret == 0); ret = fstatfs(fd_cap, &sf); CHECK_RESULT(fstatfs, CAP_FSTATFS, ret == 0); ret = fpathconf(fd_cap, _PC_NAME_MAX); CHECK_RESULT(fpathconf, CAP_FPATHCONF, ret >= 0); ret = futimes(fd_cap, NULL); CHECK_RESULT(futimes, CAP_FUTIMES, ret == 0); ret = poll(&pollfd, 1, 0); if (rights & CAP_POLL_EVENT) CHECK((pollfd.revents & POLLNVAL) == 0); else CHECK((pollfd.revents & POLLNVAL) != 0); /* XXX: select, kqueue */ close (fd_cap); return (success); }
static unsigned int call_syscall(struct syscall_desc *scall, char *argv[]) { struct stat64 sb; long long flags; unsigned int i; char *endp; int name, rval; union { char *str; long long num; } args[MAX_ARGS]; #ifdef HAS_FREEBSD_ACL int entry_id = ACL_FIRST_ENTRY; acl_t acl, newacl; acl_entry_t entry, newentry; #endif /* * Verify correctness of the arguments. */ for (i = 0; i < sizeof(args)/sizeof(args[0]); i++) { if (scall->sd_args[i] == TYPE_NONE) { if (argv[i] == NULL || strcmp(argv[i], ":") == 0) break; fprintf(stderr, "too many arguments [%s]\n", argv[i]); exit(1); } else { if (argv[i] == NULL || strcmp(argv[i], ":") == 0) { if (scall->sd_args[i] & TYPE_OPTIONAL) break; fprintf(stderr, "too few arguments\n"); exit(1); } if ((scall->sd_args[i] & TYPE_MASK) == TYPE_STRING) { if (strcmp(argv[i], "NULL") == 0) args[i].str = NULL; else if (strcmp(argv[i], "DEADCODE") == 0) args[i].str = (void *)0xdeadc0de; else args[i].str = argv[i]; } else if ((scall->sd_args[i] & TYPE_MASK) == TYPE_NUMBER) { args[i].num = strtoll(argv[i], &endp, 0); if (*endp != '\0' && !isspace((unsigned char)*endp)) { fprintf(stderr, "invalid argument %u, number expected [%s]\n", i, endp); exit(1); } } else if ((scall->sd_args[i] & TYPE_MASK) == TYPE_DESCRIPTOR) { if (strcmp(argv[i], "AT_FDCWD") == 0) { args[i].num = AT_FDCWD; } else if (strcmp(argv[i], "BADFD") == 0) { /* In case AT_FDCWD is -1 on some systems... */ if (AT_FDCWD == -1) args[i].num = -2; else args[i].num = -1; } else { int pos; pos = strtoll(argv[i], &endp, 0); if (*endp != '\0' && !isspace((unsigned char)*endp)) { fprintf(stderr, "invalid argument %u, number expected [%s]\n", i, endp); exit(1); } args[i].num = descriptor_get(pos); } } } } /* * Call the given syscall. */ #define NUM(n) (args[(n)].num) #define STR(n) (args[(n)].str) switch (scall->sd_action) { case ACTION_OPEN: flags = str2flags(open_flags, STR(1)); if (flags & O_CREAT) { if (i == 2) { fprintf(stderr, "too few arguments\n"); exit(1); } rval = open(STR(0), (int)flags, (mode_t)NUM(2)); } else { if (i == 3) { fprintf(stderr, "too many arguments\n"); exit(1); } rval = open(STR(0), (int)flags); } if (rval >= 0) descriptor_add(rval); break; case ACTION_OPENAT: flags = str2flags(open_flags, STR(2)); if (flags & O_CREAT) { if (i == 3) { fprintf(stderr, "too few arguments\n"); exit(1); } rval = openat(NUM(0), STR(1), (int)flags, (mode_t)NUM(3)); } else { if (i == 4) { fprintf(stderr, "too many arguments\n"); exit(1); } rval = openat(NUM(0), STR(1), (int)flags); } if (rval >= 0) descriptor_add(rval); break; case ACTION_CREATE: rval = open(STR(0), O_CREAT | O_EXCL, (mode_t)NUM(1)); if (rval >= 0) close(rval); break; case ACTION_UNLINK: rval = unlink(STR(0)); break; case ACTION_UNLINKAT: rval = unlinkat(NUM(0), STR(1), (int)str2flags(unlinkat_flags, STR(2))); break; case ACTION_MKDIR: rval = mkdir(STR(0), (mode_t)NUM(1)); break; case ACTION_MKDIRAT: rval = mkdirat(NUM(0), STR(1), (mode_t)NUM(2)); break; case ACTION_RMDIR: rval = rmdir(STR(0)); break; case ACTION_LINK: rval = link(STR(0), STR(1)); break; case ACTION_LINKAT: rval = linkat(NUM(0), STR(1), NUM(2), STR(3), (int)str2flags(linkat_flags, STR(4))); break; case ACTION_SYMLINK: rval = symlink(STR(0), STR(1)); break; case ACTION_SYMLINKAT: rval = symlinkat(STR(0), NUM(1), STR(2)); break; case ACTION_RENAME: rval = rename(STR(0), STR(1)); break; case ACTION_RENAMEAT: rval = renameat(NUM(0), STR(1), NUM(2), STR(3)); break; case ACTION_MKFIFO: rval = mkfifo(STR(0), (mode_t)NUM(1)); break; case ACTION_MKFIFOAT: rval = mkfifoat(NUM(0), STR(1), (mode_t)NUM(2)); break; case ACTION_MKNOD: case ACTION_MKNODAT: { mode_t ntype; dev_t dev; int fa; switch (scall->sd_action) { case ACTION_MKNOD: fa = 0; break; case ACTION_MKNODAT: fa = 1; break; default: abort(); } dev = makedev(NUM(fa + 3), NUM(fa + 4)); if (strcmp(STR(fa + 1), "c") == 0) /* character device */ ntype = S_IFCHR; else if (strcmp(STR(fa + 1), "b") == 0) /* block device */ ntype = S_IFBLK; else if (strcmp(STR(fa + 1), "f") == 0) /* fifo special */ ntype = S_IFIFO; else if (strcmp(STR(fa + 1), "d") == 0) /* directory */ ntype = S_IFDIR; else if (strcmp(STR(fa + 1), "o") == 0) /* regular file */ ntype = S_IFREG; else { fprintf(stderr, "wrong argument 1\n"); exit(1); } switch (scall->sd_action) { case ACTION_MKNOD: rval = mknod(STR(0), ntype | NUM(2), dev); break; case ACTION_MKNODAT: rval = mknodat(NUM(0), STR(1), ntype | NUM(3), dev); break; default: abort(); } break; } case ACTION_BIND: { struct sockaddr_un sunx; sunx.sun_family = AF_UNIX; strncpy(sunx.sun_path, STR(0), sizeof(sunx.sun_path) - 1); sunx.sun_path[sizeof(sunx.sun_path) - 1] = '\0'; rval = socket(AF_UNIX, SOCK_STREAM, 0); if (rval < 0) break; rval = bind(rval, (struct sockaddr *)&sunx, sizeof(sunx)); break; } #ifdef HAS_BINDAT case ACTION_BINDAT: { struct sockaddr_un sunx; sunx.sun_family = AF_UNIX; strncpy(sunx.sun_path, STR(1), sizeof(sunx.sun_path) - 1); sunx.sun_path[sizeof(sunx.sun_path) - 1] = '\0'; rval = socket(AF_UNIX, SOCK_STREAM, 0); if (rval < 0) break; rval = bindat(NUM(0), rval, (struct sockaddr *)&sunx, sizeof(sunx)); break; } #endif case ACTION_CONNECT: { struct sockaddr_un sunx; sunx.sun_family = AF_UNIX; strncpy(sunx.sun_path, STR(0), sizeof(sunx.sun_path) - 1); sunx.sun_path[sizeof(sunx.sun_path) - 1] = '\0'; rval = socket(AF_UNIX, SOCK_STREAM, 0); if (rval < 0) break; rval = connect(rval, (struct sockaddr *)&sunx, sizeof(sunx)); break; } #ifdef HAS_CONNECTAT case ACTION_CONNECTAT: { struct sockaddr_un sunx; sunx.sun_family = AF_UNIX; strncpy(sunx.sun_path, STR(1), sizeof(sunx.sun_path) - 1); sunx.sun_path[sizeof(sunx.sun_path) - 1] = '\0'; rval = socket(AF_UNIX, SOCK_STREAM, 0); if (rval < 0) break; rval = connectat(NUM(0), rval, (struct sockaddr *)&sunx, sizeof(sunx)); break; } #endif case ACTION_CHMOD: rval = chmod(STR(0), (mode_t)NUM(1)); break; case ACTION_FCHMOD: rval = fchmod(NUM(0), (mode_t)NUM(1)); break; #ifdef HAS_LCHMOD case ACTION_LCHMOD: rval = lchmod(STR(0), (mode_t)NUM(1)); break; #endif case ACTION_FCHMODAT: rval = fchmodat(NUM(0), STR(1), (mode_t)NUM(2), str2flags(fchmodat_flags, STR(3))); break; case ACTION_CHOWN: rval = chown(STR(0), (uid_t)NUM(1), (gid_t)NUM(2)); break; case ACTION_FCHOWN: rval = fchown(NUM(0), (uid_t)NUM(1), (gid_t)NUM(2)); break; case ACTION_LCHOWN: rval = lchown(STR(0), (uid_t)NUM(1), (gid_t)NUM(2)); break; case ACTION_FCHOWNAT: rval = fchownat(NUM(0), STR(1), (uid_t)NUM(2), (gid_t)NUM(3), (int)str2flags(fchownat_flags, STR(4))); break; #ifdef HAS_CHFLAGS case ACTION_CHFLAGS: rval = chflags(STR(0), (unsigned long)str2flags(chflags_flags, STR(1))); break; #endif #ifdef HAS_FCHFLAGS case ACTION_FCHFLAGS: rval = fchflags(NUM(0), (unsigned long)str2flags(chflags_flags, STR(1))); break; #endif #ifdef HAS_CHFLAGSAT case ACTION_CHFLAGSAT: rval = chflagsat(NUM(0), STR(1), (unsigned long)str2flags(chflags_flags, STR(2)), (int)str2flags(chflagsat_flags, STR(3))); break; #endif #ifdef HAS_LCHFLAGS case ACTION_LCHFLAGS: rval = lchflags(STR(0), (unsigned long)str2flags(chflags_flags, STR(1))); break; #endif case ACTION_TRUNCATE: rval = truncate64(STR(0), NUM(1)); break; case ACTION_FTRUNCATE: rval = ftruncate64(NUM(0), NUM(1)); break; case ACTION_STAT: rval = stat64(STR(0), &sb); if (rval == 0) { show_stats(&sb, STR(1)); return (i); } break; case ACTION_FSTAT: rval = fstat64(NUM(0), &sb); if (rval == 0) { show_stats(&sb, STR(1)); return (i); } break; case ACTION_LSTAT: rval = lstat64(STR(0), &sb); if (rval == 0) { show_stats(&sb, STR(1)); return (i); } break; case ACTION_FSTATAT: rval = fstatat(NUM(0), STR(1), &sb, (int)str2flags(fstatat_flags, STR(2))); if (rval == 0) { show_stats(&sb, STR(3)); return (i); } break; case ACTION_PATHCONF: case ACTION_FPATHCONF: #ifdef HAS_LPATHCONF case ACTION_LPATHCONF: #endif { long lrval; name = str2name(pathconf_names, STR(1)); if (name == -1) { fprintf(stderr, "unknown name %s", STR(1)); exit(1); } errno = 0; switch (scall->sd_action) { case ACTION_PATHCONF: lrval = pathconf(STR(0), name); break; case ACTION_FPATHCONF: lrval = fpathconf(NUM(0), name); break; #ifdef HAS_LPATHCONF case ACTION_LPATHCONF: lrval = lpathconf(STR(0), name); break; #endif default: abort(); } if (lrval == -1 && errno == 0) { printf("unlimited\n"); return (i); } else if (lrval >= 0) { printf("%ld\n", lrval); return (i); } rval = -1; break; } #ifdef HAS_FREEBSD_ACL case ACTION_PREPENDACL: rval = -1; acl = acl_get_file(STR(0), ACL_TYPE_NFS4); if (acl == NULL) break; newacl = acl_from_text(STR(1)); if (acl == NULL) break; while (acl_get_entry(newacl, entry_id, &newentry) == 1) { entry_id = ACL_NEXT_ENTRY; if (acl_create_entry_np(&acl, &entry, 0)) break; if (acl_copy_entry(entry, newentry)) break; } rval = acl_set_file(STR(0), ACL_TYPE_NFS4, acl); break; case ACTION_READACL: acl = acl_get_file(STR(0), ACL_TYPE_NFS4); if (acl == NULL) rval = -1; else rval = 0; break; #endif case ACTION_WRITE: rval = write(NUM(0), STR(1), strlen(STR(1))); break; default: fprintf(stderr, "unsupported syscall\n"); exit(1); } #undef STR #undef NUM if (rval < 0) { const char *serrno; serrno = err2str(errno); fprintf(stderr, "%s returned %d\n", scall->sd_name, rval); printf("%s\n", serrno); exit(1); } printf("0\n"); return (i); }
int fastcopy(char *from, char *to, struct stat *sbp) { struct timeval tval[2]; static u_int32_t blen; static char *bp; int nread, from_fd, to_fd; int badchown = 0, serrno = 0; if ((from_fd = open(from, O_RDONLY, 0)) < 0) { warn("%s", from); return (1); } if ((to_fd = open(to, O_CREAT | O_TRUNC | O_WRONLY, 0600)) < 0) { warn("%s", to); (void)close(from_fd); return (1); } if (fchown(to_fd, sbp->st_uid, sbp->st_gid)) { serrno = errno; badchown = 1; } (void) fchmod(to_fd, sbp->st_mode & ~(S_ISUID|S_ISGID)); if (!blen) { blen = sbp->st_blksize; if ((bp = malloc(blen)) == NULL) { warn(NULL); blen = 0; return (1); } } while ((nread = read(from_fd, bp, blen)) > 0) if (write(to_fd, bp, nread) != nread) { warn("%s", to); goto err; } if (nread < 0) { warn("%s", from); err: if (unlink(to)) warn("%s: remove", to); (void)close(from_fd); (void)close(to_fd); return (1); } (void)close(from_fd); if (badchown) { if ((sbp->st_mode & (S_ISUID|S_ISGID))) { warnc(serrno, "%s: set owner/group; not setting setuid/setgid", to); sbp->st_mode &= ~(S_ISUID|S_ISGID); } else if (!fflg) warnc(serrno, "%s: set owner/group", to); } if (fchmod(to_fd, sbp->st_mode)) warn("%s: set mode", to); /* * XXX * NFS doesn't support chflags; ignore errors unless there's reason * to believe we're losing bits. (Note, this still won't be right * if the server supports flags and we were trying to *remove* flags * on a file that we copied, i.e., that we didn't create.) */ errno = 0; if (fchflags(to_fd, sbp->st_flags)) if (errno != EOPNOTSUPP || sbp->st_flags != 0) warn("%s: set flags", to); TIMESPEC_TO_TIMEVAL(&tval[0], &sbp->st_atimespec); TIMESPEC_TO_TIMEVAL(&tval[1], &sbp->st_mtimespec); if (utimes(to, tval)) warn("%s: set times", to); if (close(to_fd)) { warn("%s", to); return (1); } if (unlink(from)) { warn("%s: remove", from); return (1); } return (0); }
static int loopback_fsetattr_x(const char *path, struct setattr_x *attr, struct fuse_file_info *fi) { int res; uid_t uid = -1; gid_t gid = -1; if (SETATTR_WANTS_MODE(attr)) { res = fchmod(fi->fh, attr->mode); if (res == -1) { return -errno; } } if (SETATTR_WANTS_UID(attr)) { uid = attr->uid; } if (SETATTR_WANTS_GID(attr)) { gid = attr->gid; } if ((uid != -1) || (gid != -1)) { res = fchown(fi->fh, uid, gid); if (res == -1) { return -errno; } } if (SETATTR_WANTS_SIZE(attr)) { res = ftruncate(fi->fh, attr->size); if (res == -1) { return -errno; } } if (SETATTR_WANTS_MODTIME(attr)) { struct timeval tv[2]; if (!SETATTR_WANTS_ACCTIME(attr)) { gettimeofday(&tv[0], NULL); } else { tv[0].tv_sec = attr->acctime.tv_sec; tv[0].tv_usec = attr->acctime.tv_nsec / 1000; } tv[1].tv_sec = attr->modtime.tv_sec; tv[1].tv_usec = attr->modtime.tv_nsec / 1000; res = futimes(fi->fh, tv); if (res == -1) { return -errno; } } if (SETATTR_WANTS_CRTIME(attr)) { struct attrlist attributes; attributes.bitmapcount = ATTR_BIT_MAP_COUNT; attributes.reserved = 0; attributes.commonattr = ATTR_CMN_CRTIME; attributes.dirattr = 0; attributes.fileattr = 0; attributes.forkattr = 0; attributes.volattr = 0; res = fsetattrlist(fi->fh, &attributes, &attr->crtime, sizeof(struct timespec), FSOPT_NOFOLLOW); if (res == -1) { return -errno; } } if (SETATTR_WANTS_CHGTIME(attr)) { struct attrlist attributes; attributes.bitmapcount = ATTR_BIT_MAP_COUNT; attributes.reserved = 0; attributes.commonattr = ATTR_CMN_CHGTIME; attributes.dirattr = 0; attributes.fileattr = 0; attributes.forkattr = 0; attributes.volattr = 0; res = fsetattrlist(fi->fh, &attributes, &attr->chgtime, sizeof(struct timespec), FSOPT_NOFOLLOW); if (res == -1) { return -errno; } } if (SETATTR_WANTS_BKUPTIME(attr)) { struct attrlist attributes; attributes.bitmapcount = ATTR_BIT_MAP_COUNT; attributes.reserved = 0; attributes.commonattr = ATTR_CMN_BKUPTIME; attributes.dirattr = 0; attributes.fileattr = 0; attributes.forkattr = 0; attributes.volattr = 0; res = fsetattrlist(fi->fh, &attributes, &attr->bkuptime, sizeof(struct timespec), FSOPT_NOFOLLOW); if (res == -1) { return -errno; } } if (SETATTR_WANTS_FLAGS(attr)) { res = fchflags(fi->fh, attr->flags); if (res == -1) { return -errno; } } return 0; }