Blob


1 #include <u.h>
2 #define NOPLAN9DEFINES
3 #include <libc.h>
4 #include <sys/stat.h>
5 #include <dirent.h>
7 extern int _p9dir(struct stat*, struct stat*, char*, Dir*, char**, char*);
9 #if defined(__linux__)
10 static int
11 mygetdents(int fd, struct dirent *buf, int n)
12 {
13 off_t off;
14 int nn;
16 /* This doesn't match the man page, but it works in Debian with a 2.2 kernel */
17 off = p9seek(fd, 0, 1);
18 nn = getdirentries(fd, (void*)buf, n, &off);
19 return nn;
20 }
21 #elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
22 static int
23 mygetdents(int fd, struct dirent *buf, int n)
24 {
25 long off;
26 return getdirentries(fd, (void*)buf, n, &off);
27 }
28 #elif defined(__sun__)
29 static int
30 mygetdents(int fd, struct dirent *buf, int n)
31 {
32 return getdents(fd, (void*)buf, n);
33 }
34 #endif
36 static int
37 countde(char *p, int n)
38 {
39 char *e;
40 int m;
41 struct dirent *de;
43 e = p+n;
44 m = 0;
45 while(p < e){
46 de = (struct dirent*)p;
47 if(de->d_reclen <= 4+2+2+1 || p+de->d_reclen > e)
48 break;
49 if(de->d_name[0]=='.' && de->d_name[1]==0)
50 de->d_name[0] = 0;
51 else if(de->d_name[0]=='.' && de->d_name[1]=='.' && de->d_name[2]==0)
52 de->d_name[0] = 0;
53 m++;
54 p += de->d_reclen;
55 }
56 return m;
57 }
59 static int
60 dirpackage(int fd, char *buf, int n, Dir **dp)
61 {
62 int oldwd;
63 char *p, *str, *estr;
64 int i, nstr, m;
65 struct dirent *de;
66 struct stat st, lst;
67 Dir *d;
69 n = countde(buf, n);
70 if(n <= 0)
71 return n;
73 if((oldwd = open(".", O_RDONLY)) < 0)
74 return -1;
75 if(fchdir(fd) < 0)
76 return -1;
78 p = buf;
79 nstr = 0;
81 for(i=0; i<n; i++){
82 de = (struct dirent*)p;
83 memset(&lst, 0, sizeof lst);
84 if(de->d_name[0] == 0)
85 /* nothing */ {}
86 else if(lstat(de->d_name, &lst) < 0)
87 de->d_name[0] = 0;
88 else{
89 st = lst;
90 if(S_ISLNK(lst.st_mode))
91 stat(de->d_name, &st);
92 nstr += _p9dir(&lst, &st, de->d_name, nil, nil, nil);
93 }
94 p += de->d_reclen;
95 }
97 d = malloc(sizeof(Dir)*n+nstr);
98 if(d == nil){
99 fchdir(oldwd);
100 close(oldwd);
101 return -1;
103 str = (char*)&d[n];
104 estr = str+nstr;
106 p = buf;
107 m = 0;
108 for(i=0; i<n; i++){
109 de = (struct dirent*)p;
110 if(de->d_name[0] != 0 && lstat(de->d_name, &lst) >= 0){
111 st = lst;
112 if((lst.st_mode&S_IFMT) == S_IFLNK)
113 stat(de->d_name, &st);
114 _p9dir(&lst, &st, de->d_name, &d[m++], &str, estr);
116 p += de->d_reclen;
119 fchdir(oldwd);
120 close(oldwd);
121 *dp = d;
122 return m;
125 long
126 dirread(int fd, Dir **dp)
128 char *buf;
129 struct stat st;
130 int n;
132 *dp = 0;
134 if(fstat(fd, &st) < 0)
135 return -1;
137 if(st.st_blksize < 8192)
138 st.st_blksize = 8192;
140 buf = malloc(st.st_blksize);
141 if(buf == nil)
142 return -1;
144 n = mygetdents(fd, (void*)buf, st.st_blksize);
145 if(n < 0){
146 free(buf);
147 return -1;
149 n = dirpackage(fd, buf, n, dp);
150 free(buf);
151 return n;
155 long
156 dirreadall(int fd, Dir **d)
158 uchar *buf, *nbuf;
159 long n, ts;
160 struct stat st;
162 if(fstat(fd, &st) < 0)
163 return -1;
165 if(st.st_blksize < 8192)
166 st.st_blksize = 8192;
168 buf = nil;
169 ts = 0;
170 for(;;){
171 nbuf = realloc(buf, ts+st.st_blksize);
172 if(nbuf == nil){
173 free(buf);
174 return -1;
176 buf = nbuf;
177 n = mygetdents(fd, (void*)(buf+ts), st.st_blksize);
178 if(n <= 0)
179 break;
180 ts += n;
182 if(ts >= 0)
183 ts = dirpackage(fd, (char*)buf, ts, d);
184 free(buf);
185 if(ts == 0 && n < 0)
186 return -1;
187 return ts;