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