plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

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 }