Add zfsaddhomedir
[public/pyceo-broken.git] / src / zfsaddhomedir.c
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <dirent.h>
4 #include <limits.h>
5 #include <sys/stat.h>
6 #include <sys/types.h>
7 #include <fcntl.h>
8 #include <assert.h>
9 #include "util.h"
10
11 int main(int argc, char *argv[]) {
12     if(argc < 6) {
13         fprintf(stderr, "Usage: zfsaddhomedir homedir skeldir uid gid mode acl\n");
14         return 1;
15     }
16
17     // TODO: check return of spawnv
18     {
19         char *homedir = argv[1];
20         char *skeldir = argv[2];
21         char *mode = argv[5];
22         char *acl = (argc >= 7) ? argv[6] : NULL;
23         uid_t uid, gid;
24         char *zfs_bin = "/usr/sbin/zfs";
25         char *chmod_bin = "/usr/bin/chmod";
26         char *dataset = homedir + 1;
27         char *create_argv[] = { "zfs", "create", dataset, NULL };
28         char *quota_argv[] = { "zfs", "set", "quota=3G", dataset, NULL };
29         char *mode_argv[] = { "chmod", mode, homedir, NULL };
30         char *acl_argv[] = { "chmod", acl, homedir, NULL };
31         DIR *skel;
32         struct dirent *skelent;
33
34         assert(homedir[0]);
35         uid = atol(argv[3]);
36         gid = atol(argv[4]);
37
38         if(spawnv(zfs_bin, create_argv))
39             return 1;
40         if(spawnv(zfs_bin, quota_argv))
41             return 1;
42         if(spawnv(chmod_bin, mode_argv))
43             return 1;
44         if(acl && spawnv(chmod_bin, acl_argv))
45             return 1;
46
47         skel = opendir(skeldir);
48         if (!skel) {
49             errorpe("failed to open %s", skeldir);
50             return -1;
51         }
52
53         while ((skelent = readdir(skel))) {
54             struct stat sb;
55             char src[PATH_MAX], dest[PATH_MAX];
56
57             if (!strcmp(skelent->d_name, ".") || !strcmp(skelent->d_name, ".."))
58                 continue;
59
60             snprintf(src, sizeof(src), "%s/%s", skeldir, skelent->d_name);
61             snprintf(dest, sizeof(dest), "/%s/%s", homedir, skelent->d_name);
62             lstat(src, &sb);
63
64             if (sb.st_uid || sb.st_gid) {
65                 warn("not creating %s due to ownership", dest);
66                 continue;
67             }
68
69             if (S_ISREG(sb.st_mode)) {
70                 int bytes;
71                 char buf[4096];
72
73                 int srcfd = open(src, O_RDONLY);
74                 if (srcfd == -1) {
75                     warnpe("open: %s", src);
76                     continue;
77                 }
78
79                 int destfd = open(dest, O_WRONLY|O_CREAT|O_EXCL, sb.st_mode & 0777);
80                 if (destfd == -1) {
81                     warnpe("open: %s", dest);
82                     close(srcfd);
83                     continue;
84                 }
85
86                 for (;;) {
87                     bytes = read(srcfd, buf, sizeof(buf));
88                     if (!bytes)
89                         break;
90                     if (bytes < 0) {
91                         warnpe("read");
92                         break;
93                     }
94                     if (write(destfd, buf, bytes) < 0) {
95                         warnpe("write");
96                         break;
97                     }
98                 }
99             if (fchown(destfd, uid, gid))
100                 errorpe("chown: %s", dest);
101
102                 close(srcfd);
103                 close(destfd);
104             } else if (S_ISDIR(sb.st_mode)) {
105                 if (mkdir(dest, sb.st_mode & 0777)) {
106                     warnpe("mkdir: %s", dest);
107                     continue;
108                 }
109                 if (chown(dest, uid, gid))
110                     errorpe("chown: %s", dest);
111             } else if (S_ISLNK(sb.st_mode)) {
112                 char lnkdest[PATH_MAX];
113                 int bytes;
114                 bytes = readlink(src, lnkdest, sizeof(lnkdest));
115                 lnkdest[bytes] = '\0';
116                 if (bytes == -1) {
117                     warnpe("readlink: %s", src);
118                     continue;
119                 }
120                 if (symlink(lnkdest, dest)) {
121                     warnpe("symlink: %s", dest);
122                     continue;
123                 }
124                 if (lchown(dest, uid, gid))
125                     errorpe("lchown: %s", dest);
126             } else {
127                 warn("not creating %s", dest);
128             }
129         }
130
131         closedir(skel);
132
133         if (chown(homedir, uid, gid)) {
134             errorpe("failed to chown %s", homedir);
135             return -1;
136         }
137     }
138
139     return 0;
140 }