X-Git-Url: https://git.cameronkatri.com/pw-darwin.git/blobdiff_plain/0b3bc144008c93d61f3720daf8aeff915df65ede..f80a3677a5167b9e85bbc2a622456232fd4668f5:/pw/cpdir.c diff --git a/pw/cpdir.c b/pw/cpdir.c index f8e2c32..334f789 100644 --- a/pw/cpdir.c +++ b/pw/cpdir.c @@ -22,92 +22,103 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ -#include -#include -#include -#include -#include -#include +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + #include -#include -#include +#include #include +#include +#include +#include +#include "pw.h" #include "pwupd.h" void -copymkdir(char const * dir, char const * skel, mode_t mode, uid_t uid, gid_t gid) +copymkdir(int rootfd, char const * dir, int skelfd, mode_t mode, uid_t uid, + gid_t gid, int flags) { - int rc = 0; - char src[MAXPATHLEN]; - char dst[MAXPATHLEN]; - - if (mkdir(dir, mode) != 0 && errno != EEXIST) { - sprintf(src, "mkdir(%s)", dir); - perror(src); - } 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') - rc = 1; - else { - DIR *d = opendir(skel); - - if (d != NULL) { - struct dirent *e; - - while ((e = readdir(d)) != NULL) { - char *p = e->d_name; - - sprintf(src, "%s/%s", skel, p); - if (stat(src, &st) == 0) { - if (strncmp(p, "dot.", 4) == 0) /* Conversion */ - p += 3; - sprintf(dst, "%s/%s", dir, p); - 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 & 0777), uid, gid); - /* - * Note: don't propogate 'special' attributes - */ - } 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); - close(outfd); - chown(dst, uid, gid); - } - } - } - } - closedir(d); - } + 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 (--counter == 0 && copybuf != NULL) { - free(copybuf); - copybuf = NULL; + + 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); } -