Improve error handling when writing
[mspang/pyceo.git] / src / homedir.c
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <sys/acl.h>
6 #include <dirent.h>
7 #include <pwd.h>
8 #include <fcntl.h>
9
10 #include "homedir.h"
11 #include "util.h"
12 #include "config.h"
13
14 static int set_acl(char *dir, char *acl_text, acl_type_t type) {
15     acl_t acl = acl_from_text(acl_text);
16     if (acl == (acl_t)NULL) {
17         errorpe("acl_from_text: %s", acl_text);
18         return -1;
19     }
20     if (acl_set_file(dir, type, acl) != 0) {
21         errorpe("acl_set_file: %s %s 0x%X %p", acl_text, dir, (int)type, (void*)acl);
22         acl_free(acl);
23         return -1;
24     }
25     acl_free(acl);
26
27     return 0;
28 }
29
30 int ceo_create_home(char *homedir, char *skel, uid_t uid, gid_t gid, char *access_acl, char *default_acl, char *email) {
31     int mask;
32     DIR *skeldir;
33     struct dirent *skelent;
34
35     mask = umask(0);
36
37     if (mkdir(homedir, 0755)) {
38         errorpe("failed to create %s", homedir);
39         return -1;
40     }
41
42     if (access_acl && set_acl(homedir, access_acl, ACL_TYPE_ACCESS) != 0)
43         return -1;
44     if (default_acl && set_acl(homedir, default_acl, ACL_TYPE_DEFAULT) != 0)
45         return -1;
46
47     skeldir = opendir(skel);
48     if (!skeldir) {
49         errorpe("failed to open %s", skel);
50         return -1;
51     }
52
53     while ((skelent = readdir(skeldir))) {
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", skel, 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 (full_write(destfd, buf, bytes)) {
95                     warnpe("write: %s", src);
96                     break;
97                 }
98             }
99
100             if (fchown(destfd, uid, gid))
101                 errorpe("chown: %s", dest);
102
103             close(srcfd);
104             close(destfd);
105         } else if (S_ISDIR(sb.st_mode)) {
106             if (mkdir(dest, sb.st_mode & 0777)) {
107                 warnpe("mkdir: %s", dest);
108                 continue;
109             }
110             if (chown(dest, uid, gid))
111                 errorpe("chown: %s", dest);
112         } else if (S_ISLNK(sb.st_mode)) {
113             char lnkdest[PATH_MAX];
114             int bytes;
115             bytes = readlink(src, lnkdest, sizeof(lnkdest));
116             lnkdest[bytes] = '\0';
117             if (bytes == -1) {
118                 warnpe("readlink: %s", src);
119                 continue;
120             }
121             if (symlink(lnkdest, dest)) {
122                 warnpe("symlink: %s", dest);
123                 continue;
124             }
125             if (lchown(dest, uid, gid))
126                 errorpe("lchown: %s", dest);
127         } else {
128             warn("not creating %s", dest);
129         }
130     }
131
132     closedir(skeldir);
133
134     if (email && *email) {
135         char dest[PATH_MAX];
136         snprintf(dest, sizeof(dest), "%s/%s", homedir, ".forward");
137         int destfd = open(dest, O_WRONLY|O_CREAT|O_EXCL, 0644);
138
139         if (full_write(destfd, email, strlen(email)))
140             warnpe("write: %s", dest);
141
142         if (fchown(destfd, uid, gid))
143             errorpe("chown: %s", dest);
144
145         close(destfd);
146     }
147
148     if (chown(homedir, uid, gid)) {
149         errorpe("failed to chown %s", homedir);
150         return -1;
151     }
152
153     umask(mask);
154
155     return 0;
156 }