URI:
       topen.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       topen.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 }