diff options
Diffstat (limited to 'usr.sbin/pw/cpdir.c')
-rw-r--r-- | usr.sbin/pw/cpdir.c | 61 |
1 files changed, 40 insertions, 21 deletions
diff --git a/usr.sbin/pw/cpdir.c b/usr.sbin/pw/cpdir.c index 3dee8f7c43ac..979323d64342 100644 --- a/usr.sbin/pw/cpdir.c +++ b/usr.sbin/pw/cpdir.c @@ -36,53 +36,52 @@ #include "pw.h" void -copymkdir(int rootfd, char const * dir, int skelfd, mode_t mode, uid_t uid, +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; + char *p, lnk[MAXPATHLEN]; + int len, srcfd, destfd; ssize_t sz; struct stat st; struct dirent *e; DIR *d; + mode_t pumask; if (*dir == '/') dir++; + pumask = umask(0); + umask(pumask); + if (mkdirat(rootfd, dir, mode) != 0) { - mode_t pumask; if (errno != EEXIST) { warn("mkdir(%s)", dir); return; } - pumask = umask(0); - umask(pumask); - if (fchmodat(rootfd, dir, mode & ~pumask, AT_SYMLINK_NOFOLLOW) == -1) warn("chmod(%s)", dir); } - if (fchownat(rootfd, dir, uid, gid, AT_SYMLINK_NOFOLLOW) == -1) warn("chown(%s)", dir); - if (flags > 0 && chflagsat(rootfd, dir, flags, AT_SYMLINK_NOFOLLOW) == -1) warn("chflags(%s)", dir); + metalog_emit(dir, (mode | S_IFDIR) & ~pumask, uid, gid, flags); 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) { + char path[MAXPATHLEN]; + if (strcmp(e->d_name, ".") == 0 || strcmp(e->d_name, "..") == 0) continue; @@ -92,19 +91,32 @@ copymkdir(int rootfd, char const * dir, int skelfd, mode_t mode, uid_t uid, if (strncmp(p, "dot.", 4) == 0) /* Conversion */ p += 3; + (void)snprintf(path, sizeof(path), "%s/%s", dir, p); 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); + int fd; + + fd = openat(skelfd, e->d_name, O_DIRECTORY); + if (fd == -1) { + warn("openat(%s)", e->d_name); + continue; + } + copymkdir(rootfd, path, fd, 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)) + (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); + if (symlinkat(lnk, rootfd, path) != 0) + warn("symlink(%s)", path); + else if (fchownat(rootfd, path, uid, gid, + AT_SYMLINK_NOFOLLOW) != 0) + warn("chown(%s)", path); + metalog_emit_symlink(path, lnk, st.st_mode & ~pumask, + uid, gid); continue; } @@ -113,22 +125,29 @@ copymkdir(int rootfd, char const * dir, int skelfd, mode_t mode, uid_t uid, if ((srcfd = openat(skelfd, e->d_name, O_RDONLY)) == -1) continue; - destfd = openat(homefd, p, O_RDWR | O_CREAT | O_EXCL, + destfd = openat(rootfd, path, 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); + do { + sz = copy_file_range(srcfd, NULL, destfd, NULL, + SSIZE_MAX, 0); + } while (sz > 0); + if (sz < 0) + warn("copy_file_range"); close(srcfd); /* * Propagate special filesystem flags */ - fchown(destfd, uid, gid); - fchflags(destfd, st.st_flags); + if (fchown(destfd, uid, gid) != 0) + warn("chown(%s)", p); + if (fchflags(destfd, st.st_flags) != 0) + warn("chflags(%s)", p); + metalog_emit(path, st.st_mode & ~pumask, uid, gid, st.st_flags); close(destfd); } closedir(d); |