diff --git a/src/simpleaddhomedir.c b/src/simpleaddhomedir.c new file mode 100644 index 0000000..5671454 --- /dev/null +++ b/src/simpleaddhomedir.c @@ -0,0 +1,137 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "util.h" + +int main(int argc, char *argv[]) { + if(argc < 7) { + fprintf(stderr, "Usage: simpleaddhomedir homedir skeldir uid gid mode\n"); + return 1; + } + + char *homedir = argv[1]; + char *skeldir = argv[2]; + char *mode = argv[5]; + uid_t uid, gid; + char *zfs_bin = "/usr/sbin/zfs"; + char *mkdir_bin = "/bin/mkdir"; + char *chmod_bin = "/bin/chmod"; + char *dataset = homedir + 1; + char *create_argv[] = { "mkdir", dataset, NULL }; + char *mode_argv[] = { "chmod", mode, homedir, NULL }; + DIR *skel; + struct dirent *skelent; + + assert(homedir[0]); + uid = atol(argv[3]); + gid = atol(argv[4]); + + if(spawnv(mkdir_bin, create_argv)) + return 1; + //Quotas are ignored now, or so I'm told. + /* if(spawnv(zfs_bin, quota_argv)) */ + /* return 1; */ + if(spawnv(chmod_bin, mode_argv)) + return 1; + //Fuck ACLs. The instructions I got didn't include them. + /* if(acl && spawnv(chmod_bin, acl_argv)) */ + /* return 1; */ + + skel = opendir(skeldir); + if (!skel) { + errorpe("failed to open %s", skeldir); + return -1; + } + + while ((skelent = readdir(skel))) { + struct stat sb; + char src[PATH_MAX], dest[PATH_MAX]; + + if (!strcmp(skelent->d_name, ".") || !strcmp(skelent->d_name, "..")) + continue; + + snprintf(src, sizeof(src), "%s/%s", skeldir, skelent->d_name); + snprintf(dest, sizeof(dest), "/%s/%s", homedir, skelent->d_name); + lstat(src, &sb); + + if (sb.st_uid || sb.st_gid) { + warn("not creating %s due to ownership", dest); + continue; + } + + if (S_ISREG(sb.st_mode)) { + int bytes; + char buf[4096]; + + int srcfd = open(src, O_RDONLY); + if (srcfd == -1) { + warnpe("open: %s", src); + continue; + } + + int destfd = open(dest, O_WRONLY|O_CREAT|O_EXCL, sb.st_mode & 0777); + if (destfd == -1) { + warnpe("open: %s", dest); + close(srcfd); + continue; + } + + for (;;) { + bytes = read(srcfd, buf, sizeof(buf)); + if (!bytes) + break; + if (bytes < 0) { + warnpe("read"); + break; + } + if (write(destfd, buf, bytes) < 0) { + warnpe("write"); + break; + } + } + if (fchown(destfd, uid, gid)) + errorpe("chown: %s", dest); + + close(srcfd); + close(destfd); + } else if (S_ISDIR(sb.st_mode)) { + if (mkdir(dest, sb.st_mode & 0777)) { + warnpe("mkdir: %s", dest); + continue; + } + if (chown(dest, uid, gid)) + errorpe("chown: %s", dest); + } else if (S_ISLNK(sb.st_mode)) { + char lnkdest[PATH_MAX]; + int bytes; + bytes = readlink(src, lnkdest, sizeof(lnkdest)); + lnkdest[bytes] = '\0'; + if (bytes == -1) { + warnpe("readlink: %s", src); + continue; + } + if (symlink(lnkdest, dest)) { + warnpe("symlink: %s", dest); + continue; + } + if (lchown(dest, uid, gid)) + errorpe("lchown: %s", dest); + } else { + warn("not creating %s", dest); + } + } + + closedir(skel); + + if (chown(homedir, uid, gid)) { + errorpe("failed to chown %s", homedir); + return -1; + } + + return 0; +}