plan9port

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

file.c (5985B)


      1 #include <u.h>
      2 #include <libc.h>
      3 #include <fcall.h>
      4 #include <thread.h>
      5 #include <9p.h>
      6 
      7 /*
      8  * To avoid deadlock, the following rules must be followed.
      9  * Always lock child then parent, never parent then child.
     10  * If holding the free file lock, do not lock any Files.
     11  */
     12 struct Filelist {
     13 	File *f;
     14 	Filelist *link;
     15 };
     16 
     17 static QLock filelk;
     18 static File *freefilelist;
     19 
     20 static File*
     21 allocfile(void)
     22 {
     23 	int i, a;
     24 	File *f;
     25 	enum { N = 16 };
     26 
     27 	qlock(&filelk);
     28 	if(freefilelist == nil){
     29 		f = emalloc9p(N*sizeof(*f));
     30 		for(i=0; i<N-1; i++)
     31 			f[i].aux = &f[i+1];
     32 		f[N-1].aux = nil;
     33 		f[0].allocd = 1;
     34 		freefilelist = f;
     35 	}
     36 
     37 	f = freefilelist;
     38 	freefilelist = f->aux;
     39 	qunlock(&filelk);
     40 
     41 	a = f->allocd;
     42 	memset(f, 0, sizeof *f);
     43 	f->allocd = a;
     44 	return f;
     45 }
     46 
     47 static void
     48 freefile(File *f)
     49 {
     50 	Filelist *fl, *flnext;
     51 
     52 	for(fl=f->filelist; fl; fl=flnext){
     53 		flnext = fl->link;
     54 		assert(fl->f == nil);
     55 		free(fl);
     56 	}
     57 
     58 	free(f->dir.name);
     59 	free(f->dir.uid);
     60 	free(f->dir.gid);
     61 	free(f->dir.muid);
     62 	qlock(&filelk);
     63 	assert(f->ref.ref == 0);
     64 	f->aux = freefilelist;
     65 	freefilelist = f;
     66 	qunlock(&filelk);
     67 }
     68 
     69 void
     70 closefile(File *f)
     71 {
     72 	if(decref(&f->ref) == 0){
     73 		f->tree->destroy(f);
     74 		freefile(f);
     75 	}
     76 }
     77 
     78 static void
     79 nop(File *f)
     80 {
     81 	USED(f);
     82 }
     83 
     84 int
     85 removefile(File *f)
     86 {
     87 	File *fp;
     88 	Filelist *fl;
     89 
     90 	fp = f->parent;
     91 	if(fp == nil){
     92 		werrstr("no parent");
     93 		closefile(f);
     94 		return -1;
     95 	}
     96 
     97 	if(fp == f){
     98 		werrstr("cannot remove root");
     99 		closefile(f);
    100 		return -1;
    101 	}
    102 
    103 	wlock(&fp->rwlock);
    104 	wlock(&f->rwlock);
    105 	if(f->nchild != 0){
    106 		werrstr("has children");
    107 		wunlock(&f->rwlock);
    108 		wunlock(&fp->rwlock);
    109 		closefile(f);
    110 		return -1;
    111 	}
    112 
    113 	if(f->parent != fp){
    114 		werrstr("parent changed underfoot");
    115 		wunlock(&f->rwlock);
    116 		wunlock(&fp->rwlock);
    117 		closefile(f);
    118 		return -1;
    119 	}
    120 
    121 	for(fl=fp->filelist; fl; fl=fl->link)
    122 		if(fl->f == f)
    123 			break;
    124 	assert(fl != nil && fl->f == f);
    125 
    126 	fl->f = nil;
    127 	fp->nchild--;
    128 	f->parent = nil;
    129 	wunlock(&fp->rwlock);
    130 	wunlock(&f->rwlock);
    131 
    132 	closefile(fp);	/* reference from child */
    133 	closefile(f);	/* reference from tree */
    134 	closefile(f);
    135 	return 0;
    136 }
    137 
    138 File*
    139 createfile(File *fp, char *name, char *uid, ulong perm, void *aux)
    140 {
    141 	File *f;
    142 	Filelist *fl, *freel;
    143 	Tree *t;
    144 
    145 	if((fp->dir.qid.type&QTDIR) == 0){
    146 		werrstr("create in non-directory");
    147 		return nil;
    148 	}
    149 
    150 	freel = nil;
    151 	wlock(&fp->rwlock);
    152 	for(fl=fp->filelist; fl; fl=fl->link){
    153 		if(fl->f == nil)
    154 			freel = fl;
    155 		else if(strcmp(fl->f->dir.name, name) == 0){
    156 			wunlock(&fp->rwlock);
    157 			werrstr("file already exists");
    158 			return nil;
    159 		}
    160 	}
    161 
    162 	if(freel == nil){
    163 		freel = emalloc9p(sizeof *freel);
    164 		freel->link = fp->filelist;
    165 		fp->filelist = freel;
    166 	}
    167 
    168 	f = allocfile();
    169 	f->dir.name = estrdup9p(name);
    170 	f->dir.uid = estrdup9p(uid ? uid : fp->dir.uid);
    171 	f->dir.gid = estrdup9p(fp->dir.gid);
    172 	f->dir.muid = estrdup9p(uid ? uid : "unknown");
    173 	f->aux = aux;
    174 	f->dir.mode = perm;
    175 
    176 	t = fp->tree;
    177 	lock(&t->genlock);
    178 	f->dir.qid.path = t->qidgen++;
    179 	unlock(&t->genlock);
    180 	if(perm & DMDIR)
    181 		f->dir.qid.type |= QTDIR;
    182 	if(perm & DMAPPEND)
    183 		f->dir.qid.type |= QTAPPEND;
    184 	if(perm & DMEXCL)
    185 		f->dir.qid.type |= QTEXCL;
    186 
    187 	f->dir.mode = perm;
    188 	f->dir.atime = f->dir.mtime = time(0);
    189 	f->dir.length = 0;
    190 	f->parent = fp;
    191 	incref(&fp->ref);
    192 	f->tree = fp->tree;
    193 
    194 	incref(&f->ref);	/* being returned */
    195 	incref(&f->ref);	/* for the tree */
    196 	freel->f = f;
    197 	fp->nchild++;
    198 	wunlock(&fp->rwlock);
    199 
    200 	return f;
    201 }
    202 
    203 static File*
    204 walkfile1(File *dir, char *elem)
    205 {
    206 	File *fp;
    207 	Filelist *fl;
    208 
    209 	rlock(&dir->rwlock);
    210 	if(strcmp(elem, "..") == 0){
    211 		fp = dir->parent;
    212 		incref(&fp->ref);
    213 		runlock(&dir->rwlock);
    214 		closefile(dir);
    215 		return fp;
    216 	}
    217 
    218 	fp = nil;
    219 	for(fl=dir->filelist; fl; fl=fl->link)
    220 		if(fl->f && strcmp(fl->f->dir.name, elem)==0){
    221 			fp = fl->f;
    222 			incref(&fp->ref);
    223 			break;
    224 		}
    225 
    226 	runlock(&dir->rwlock);
    227 	closefile(dir);
    228 	return fp;
    229 }
    230 
    231 File*
    232 walkfile(File *f, char *path)
    233 {
    234 	char *os, *s, *nexts;
    235 
    236 	if(strchr(path, '/') == nil)
    237 		return walkfile1(f, path);	/* avoid malloc */
    238 
    239 	os = s = estrdup9p(path);
    240 	for(; *s; s=nexts){
    241 		if(nexts = strchr(s, '/'))
    242 			*nexts++ = '\0';
    243 		else
    244 			nexts = s+strlen(s);
    245 		f = walkfile1(f, s);
    246 		if(f == nil)
    247 			break;
    248 	}
    249 	free(os);
    250 	return f;
    251 }
    252 
    253 static Qid
    254 mkqid(vlong path, long vers, int type)
    255 {
    256 	Qid q;
    257 
    258 	q.path = path;
    259 	q.vers = vers;
    260 	q.type = type;
    261 	return q;
    262 }
    263 
    264 
    265 Tree*
    266 alloctree(char *uid, char *gid, ulong mode, void (*destroy)(File*))
    267 {
    268 	char *muid;
    269 	Tree *t;
    270 	File *f;
    271 
    272 	t = emalloc9p(sizeof *t);
    273 	f = allocfile();
    274 	f->dir.name = estrdup9p("/");
    275 	if(uid == nil){
    276 		if(uid = getuser())
    277 			uid = estrdup9p(uid);
    278 	}
    279 	if(uid == nil)
    280 		uid = estrdup9p("none");
    281 	else
    282 		uid = estrdup9p(uid);
    283 
    284 	if(gid == nil)
    285 		gid = estrdup9p(uid);
    286 	else
    287 		gid = estrdup9p(gid);
    288 
    289 	muid = estrdup9p(uid);
    290 
    291 	f->dir.qid = mkqid(0, 0, QTDIR);
    292 	f->dir.length = 0;
    293 	f->dir.atime = f->dir.mtime = time(0);
    294 	f->dir.mode = DMDIR | mode;
    295 	f->tree = t;
    296 	f->parent = f;
    297 	f->dir.uid = uid;
    298 	f->dir.gid = gid;
    299 	f->dir.muid = muid;
    300 
    301 	incref(&f->ref);
    302 	t->root = f;
    303 	t->qidgen = 0;
    304 	t->dirqidgen = 1;
    305 	if(destroy == nil)
    306 		destroy = nop;
    307 	t->destroy = destroy;
    308 
    309 	return t;
    310 }
    311 
    312 static void
    313 _freefiles(File *f)
    314 {
    315 	Filelist *fl, *flnext;
    316 
    317 	for(fl=f->filelist; fl; fl=flnext){
    318 		flnext = fl->link;
    319 		_freefiles(fl->f);
    320 		free(fl);
    321 	}
    322 
    323 	f->tree->destroy(f);
    324 	freefile(f);
    325 }
    326 
    327 void
    328 freetree(Tree *t)
    329 {
    330 	_freefiles(t->root);
    331 	free(t);
    332 }
    333 
    334 struct Readdir {
    335 	Filelist *fl;
    336 };
    337 
    338 Readdir*
    339 opendirfile(File *dir)
    340 {
    341 	Readdir *r;
    342 
    343 	rlock(&dir->rwlock);
    344 	if((dir->dir.mode & DMDIR)==0){
    345 		runlock(&dir->rwlock);
    346 		return nil;
    347 	}
    348 	r = emalloc9p(sizeof(*r));
    349 
    350 	/*
    351 	 * This reference won't go away while we're using it
    352 	 * since we are dir->rdir.
    353 	 */
    354 	r->fl = dir->filelist;
    355 	runlock(&dir->rwlock);
    356 	return r;
    357 }
    358 
    359 long
    360 readdirfile(Readdir *r, uchar *buf, long n)
    361 {
    362 	long x, m;
    363 	Filelist *fl;
    364 
    365 	for(fl=r->fl, m=0; fl && m+2<=n; fl=fl->link, m+=x){
    366 		if(fl->f == nil)
    367 			x = 0;
    368 		else if((x=convD2M(&fl->f->dir, buf+m, n-m)) <= BIT16SZ)
    369 			break;
    370 	}
    371 	r->fl = fl;
    372 	return m;
    373 }
    374 
    375 void
    376 closedirfile(Readdir *r)
    377 {
    378 	free(r);
    379 }