1 #define _GNU_SOURCE /* for Linux O_DIRECT */
2 #include <u.h>
3 #include <dirent.h>
4 #include <errno.h>
5 #include <sys/file.h>
6 #include <sys/stat.h>
7 #define NOPLAN9DEFINES
8 #include <libc.h>
9
10 static struct {
11 Lock lk;
12 DIR **d;
13 int nd;
14 } dirs;
15
16 static int
dirput(int fd,DIR * d)17 dirput(int fd, DIR *d)
18 {
19 int i, nd;
20 DIR **dp;
21
22 if(fd < 0) {
23 werrstr("invalid fd");
24 return -1;
25 }
26 lock(&dirs.lk);
27 if(fd >= dirs.nd) {
28 nd = dirs.nd*2;
29 if(nd <= fd)
30 nd = fd+1;
31 dp = realloc(dirs.d, nd*sizeof dirs.d[0]);
32 if(dp == nil) {
33 werrstr("out of memory");
34 unlock(&dirs.lk);
35 return -1;
36 }
37 for(i=dirs.nd; i<nd; i++)
38 dp[i] = nil;
39 dirs.d = dp;
40 dirs.nd = nd;
41 }
42 dirs.d[fd] = d;
43 unlock(&dirs.lk);
44 return 0;
45 }
46
47 static DIR*
dirget(int fd)48 dirget(int fd)
49 {
50 DIR *d;
51
52 lock(&dirs.lk);
53 d = nil;
54 if(0 <= fd && fd < dirs.nd)
55 d = dirs.d[fd];
56 unlock(&dirs.lk);
57 return d;
58 }
59
60 static DIR*
dirdel(int fd)61 dirdel(int fd)
62 {
63 DIR *d;
64
65 lock(&dirs.lk);
66 d = nil;
67 if(0 <= fd && fd < dirs.nd) {
68 d = dirs.d[fd];
69 dirs.d[fd] = nil;
70 }
71 unlock(&dirs.lk);
72 return d;
73 }
74
75 int
p9create(char * path,int mode,ulong perm)76 p9create(char *path, int mode, ulong perm)
77 {
78 int fd, cexec, umode, rclose, lock, rdwr;
79 struct flock fl;
80
81 rdwr = mode&3;
82 lock = mode&OLOCK;
83 cexec = mode&OCEXEC;
84 rclose = mode&ORCLOSE;
85 mode &= ~(ORCLOSE|OCEXEC|OLOCK);
86
87 /* XXX should get mode mask right? */
88 fd = -1;
89 if(perm&DMDIR){
90 if(mode != OREAD){
91 werrstr("bad mode in directory create");
92 goto out;
93 }
94 if(mkdir(path, perm&0777) < 0)
95 goto out;
96 fd = open(path, O_RDONLY);
97 }else{
98 umode = (mode&3)|O_CREAT|O_TRUNC;
99 mode &= ~(3|OTRUNC);
100 if(mode&ODIRECT){
101 umode |= O_DIRECT;
102 mode &= ~ODIRECT;
103 }
104 if(mode&OEXCL){
105 umode |= O_EXCL;
106 mode &= ~OEXCL;
107 }
108 if(mode&OAPPEND){
109 umode |= O_APPEND;
110 mode &= ~OAPPEND;
111 }
112 if(mode){
113 werrstr("unsupported mode in create");
114 goto out;
115 }
116 fd = open(path, umode, perm);
117 }
118 out:
119 if(fd >= 0){
120 if(lock){
121 fl.l_type = (rdwr==OREAD) ? F_RDLCK : F_WRLCK;
122 fl.l_whence = SEEK_SET;
123 fl.l_start = 0;
124 fl.l_len = 0;
125 if(fcntl(fd, F_SETLK, &fl) < 0){
126 close(fd);
127 werrstr("lock: %r");
128 return -1;
129 }
130 }
131 if(cexec)
132 fcntl(fd, F_SETFL, FD_CLOEXEC);
133 if(rclose)
134 remove(path);
135 }
136 return fd;
137 }
138
139 int
p9open(char * name,int mode)140 p9open(char *name, int mode)
141 {
142 int cexec, rclose;
143 int fd, umode, lock, rdwr;
144 struct flock fl;
145 struct stat st;
146 DIR *d;
147
148 rdwr = mode&3;
149 umode = rdwr;
150 cexec = mode&OCEXEC;
151 rclose = mode&ORCLOSE;
152 lock = mode&OLOCK;
153 mode &= ~(3|OCEXEC|ORCLOSE|OLOCK);
154 if(mode&OTRUNC){
155 umode |= O_TRUNC;
156 mode ^= OTRUNC;
157 }
158 if(mode&ODIRECT){
159 umode |= O_DIRECT;
160 mode ^= ODIRECT;
161 }
162 if(mode&ONONBLOCK){
163 umode |= O_NONBLOCK;
164 mode ^= ONONBLOCK;
165 }
166 if(mode&OAPPEND){
167 umode |= O_APPEND;
168 mode ^= OAPPEND;
169 }
170 if(mode){
171 werrstr("mode 0x%x not supported", mode);
172 return -1;
173 }
174 fd = open(name, umode);
175 if(fd >= 0){
176 if(lock){
177 fl.l_type = (rdwr==OREAD) ? F_RDLCK : F_WRLCK;
178 fl.l_whence = SEEK_SET;
179 fl.l_start = 0;
180 fl.l_len = 0;
181 if(fcntl(fd, F_SETLK, &fl) < 0){
182 close(fd);
183 werrstr("lock: %r");
184 return -1;
185 }
186 }
187 if(cexec)
188 fcntl(fd, F_SETFL, FD_CLOEXEC);
189 if(fstat(fd, &st) >= 0 && S_ISDIR(st.st_mode)) {
190 d = fdopendir(fd);
191 if(d == nil) {
192 close(fd);
193 return -1;
194 }
195 if(dirput(fd, d) < 0) {
196 closedir(d);
197 return -1;
198 }
199 }
200 if(rclose)
201 remove(name);
202 }
203 return fd;
204 }
205
206 vlong
p9seek(int fd,vlong offset,int whence)207 p9seek(int fd, vlong offset, int whence)
208 {
209 DIR *d;
210
211 if((d = dirget(fd)) != nil) {
212 if(whence == 1 && offset == 0)
213 return telldir(d);
214 if(whence == 0) {
215 seekdir(d, offset);
216 return 0;
217 }
218 werrstr("bad seek in directory");
219 return -1;
220 }
221
222 return lseek(fd, offset, whence);
223 }
224
225 int
p9close(int fd)226 p9close(int fd)
227 {
228 DIR *d;
229
230 if((d = dirdel(fd)) != nil)
231 return closedir(d);
232 return close(fd);
233 }
234
235 typedef struct DirBuild DirBuild;
236 struct DirBuild {
237 Dir *d;
238 int nd;
239 int md;
240 char *str;
241 char *estr;
242 };
243
244 extern int _p9dir(struct stat*, struct stat*, char*, Dir*, char**, char*);
245
246 static int
dirbuild1(DirBuild * b,struct stat * lst,struct stat * st,char * name)247 dirbuild1(DirBuild *b, struct stat *lst, struct stat *st, char *name)
248 {
249 int i, nstr;
250 Dir *d;
251 int md, mstr;
252 char *lo, *hi, *newlo;
253
254 nstr = _p9dir(lst, st, name, nil, nil, nil);
255 if(b->md-b->nd < 1 || b->estr-b->str < nstr) {
256 // expand either d space or str space or both.
257 md = b->md;
258 if(b->md-b->nd < 1) {
259 md *= 2;
260 if(md < 16)
261 md = 16;
262 }
263 mstr = b->estr-(char*)&b->d[b->md];
264 if(b->estr-b->str < nstr) {
265 mstr += nstr;
266 mstr += mstr/2;
267 }
268 if(mstr < 512)
269 mstr = 512;
270 d = realloc(b->d, md*sizeof d[0] + mstr);
271 if(d == nil)
272 return -1;
273 // move strings and update pointers in Dirs
274 lo = (char*)&b->d[b->md];
275 newlo = (char*)&d[md];
276 hi = b->str;
277 memmove(newlo, lo+((char*)d-(char*)b->d), hi-lo);
278 for(i=0; i<b->nd; i++) {
279 if(lo <= d[i].name && d[i].name < hi)
280 d[i].name += newlo - lo;
281 if(lo <= d[i].uid && d[i].uid < hi)
282 d[i].uid += newlo - lo;
283 if(lo <= d[i].gid && d[i].gid < hi)
284 d[i].gid += newlo - lo;
285 if(lo <= d[i].muid && d[i].muid < hi)
286 d[i].muid += newlo - lo;
287 }
288 b->d = d;
289 b->md = md;
290 b->str += newlo - lo;
291 b->estr = newlo + mstr;
292 }
293 _p9dir(lst, st, name, &b->d[b->nd], &b->str, b->estr);
294 b->nd++;
295 return 0;
296 }
297
298 static long
dirreadmax(int fd,Dir ** dp,int max)299 dirreadmax(int fd, Dir **dp, int max)
300 {
301 int i;
302 DIR *dir;
303 DirBuild b;
304 struct dirent *de;
305 struct stat st, lst;
306
307 if((dir = dirget(fd)) == nil) {
308 werrstr("not a directory");
309 return -1;
310 }
311
312 memset(&b, 0, sizeof b);
313 for(i=0; max == -1 || i<max; i++) { // max = not too many, not too few
314 errno = 0;
315 de = readdir(dir);
316 if(de == nil) {
317 if(b.nd == 0 && errno != 0)
318 return -1;
319 break;
320 }
321 // Note: not all systems have d_namlen. Assume NUL-terminated.
322 if(de->d_name[0]=='.' && de->d_name[1]==0)
323 continue;
324 if(de->d_name[0]=='.' && de->d_name[1]=='.' && de->d_name[2]==0)
325 continue;
326 if(fstatat(fd, de->d_name, &lst, AT_SYMLINK_NOFOLLOW) < 0)
327 continue;
328 st = lst;
329 if(S_ISLNK(lst.st_mode))
330 fstatat(fd, de->d_name, &st, 0);
331 dirbuild1(&b, &lst, &st, de->d_name);
332 }
333 *dp = b.d;
334 return b.nd;
335 }
336
337 long
dirread(int fd,Dir ** dp)338 dirread(int fd, Dir **dp)
339 {
340 return dirreadmax(fd, dp, 10);
341 }
342
343 long
dirreadall(int fd,Dir ** dp)344 dirreadall(int fd, Dir **dp)
345 {
346 return dirreadmax(fd, dp, -1);
347 }
348