Blob
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 NOPLAN9DEFINES8 #include <libc.h>10 static struct {11 Lock lk;12 DIR **d;13 int nd;14 } dirs;16 static int17 dirput(int fd, DIR *d)18 {19 int i, nd;20 DIR **dp;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 }47 static DIR*48 dirget(int fd)49 {50 DIR *d;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 }60 static DIR*61 dirdel(int fd)62 {63 DIR *d;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 }75 int76 p9create(char *path, int mode, ulong perm)77 {78 int fd, cexec, umode, rclose, lock, rdwr;79 struct flock fl;81 rdwr = mode&3;82 lock = mode&OLOCK;83 cexec = mode&OCEXEC;84 rclose = mode&ORCLOSE;85 mode &= ~(ORCLOSE|OCEXEC|OLOCK);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 }139 int140 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;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 }206 vlong207 p9seek(int fd, vlong offset, int whence)208 {209 DIR *d;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 }222 return lseek(fd, offset, whence);223 }225 int226 p9close(int fd)227 {228 DIR *d;230 if((d = dirdel(fd)) != nil)231 return closedir(d);232 return close(fd);233 }235 typedef struct DirBuild DirBuild;236 struct DirBuild {237 Dir *d;238 int nd;239 int md;240 char *str;241 char *estr;242 };244 extern int _p9dir(struct stat*, struct stat*, char*, Dir*, char**, char*);246 static int247 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;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 Dirs274 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 }298 static long299 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;307 if((dir = dirget(fd)) == nil) {308 werrstr("not a directory");309 return -1;310 }312 memset(&b, 0, sizeof b);313 for(i=0; max == -1 || i<max; i++) { // max = not too many, not too few314 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 }337 long338 dirread(int fd, Dir **dp)339 {340 return dirreadmax(fd, dp, 10);341 }343 long344 dirreadall(int fd, Dir **dp)345 {346 return dirreadmax(fd, dp, -1);347 }