open.c (6087B)
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 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* 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* 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 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 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 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 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 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 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 338 dirread(int fd, Dir **dp) 339 { 340 return dirreadmax(fd, dp, 10); 341 } 342 343 long 344 dirreadall(int fd, Dir **dp) 345 { 346 return dirreadmax(fd, dp, -1); 347 }