xref: /plan9-contrib/sys/src/cmd/fossil/9fid.c (revision d7aba6c3b511bc618cf0c53345848188fc02611a)
1 #include "stdinc.h"
2 
3 #include "9.h"
4 
5 static struct {
6 	QLock	lock;
7 
8 	Fid*	free;
9 	int	nfree;
10 	int	inuse;
11 } fbox;
12 
13 static void
fidLock(Fid * fid,int flags)14 fidLock(Fid* fid, int flags)
15 {
16 	if(flags & FidFWlock){
17 		wlock(&fid->lock);
18 		fid->flags = flags;
19 	}
20 	else
21 		rlock(&fid->lock);
22 
23 	/*
24 	 * Callers of file* routines are expected to lock fsys->fs->elk
25 	 * before making any calls in order to make sure the epoch doesn't
26 	 * change underfoot. With the exception of Tversion and Tattach,
27 	 * that implies all 9P functions need to lock on entry and unlock
28 	 * on exit. Fortunately, the general case is the 9P functions do
29 	 * fidGet on entry and fidPut on exit, so this is a convenient place
30 	 * to do the locking.
31 	 * No fsys->fs->elk lock is required if the fid is being created
32 	 * (Tauth, Tattach and Twalk). FidFCreate is always accompanied by
33 	 * FidFWlock so the setting and testing of FidFCreate here and in
34 	 * fidUnlock below is always done under fid->lock.
35 	 * A side effect is that fidFree is called with the fid locked, and
36 	 * must call fidUnlock only after it has disposed of any File
37 	 * resources still held.
38 	 */
39 	if(!(flags & FidFCreate))
40 		fsysFsRlock(fid->fsys);
41 }
42 
43 static void
fidUnlock(Fid * fid)44 fidUnlock(Fid* fid)
45 {
46 	if(!(fid->flags & FidFCreate))
47 		fsysFsRUnlock(fid->fsys);
48 	if(fid->flags & FidFWlock){
49 		fid->flags = 0;
50 		wunlock(&fid->lock);
51 		return;
52 	}
53 	runlock(&fid->lock);
54 }
55 
56 static Fid*
fidAlloc(void)57 fidAlloc(void)
58 {
59 	Fid *fid;
60 
61 	qlock(&fbox.lock);
62 	if(fbox.nfree > 0){
63 		fid = fbox.free;
64 		fbox.free = fid->hash;
65 		fbox.nfree--;
66 	}
67 	else{
68 		fid = vtmallocz(sizeof(Fid));
69 	}
70 	fbox.inuse++;
71 	qunlock(&fbox.lock);
72 
73 	fid->con = nil;
74 	fid->fidno = NOFID;
75 	fid->ref = 0;
76 	fid->flags = 0;
77 	fid->open = FidOCreate;
78 	assert(fid->fsys == nil);
79 	assert(fid->file == nil);
80 	fid->qid = (Qid){0, 0, 0};
81 	assert(fid->uid == nil);
82 	assert(fid->uname == nil);
83 	assert(fid->db == nil);
84 	assert(fid->excl == nil);
85 	assert(fid->rpc == nil);
86 	assert(fid->cuname == nil);
87 	fid->hash = fid->next = fid->prev = nil;
88 
89 	return fid;
90 }
91 
92 static void
fidFree(Fid * fid)93 fidFree(Fid* fid)
94 {
95 	if(fid->file != nil){
96 		fileDecRef(fid->file);
97 		fid->file = nil;
98 	}
99 	if(fid->db != nil){
100 		dirBufFree(fid->db);
101 		fid->db = nil;
102 	}
103 	fidUnlock(fid);
104 
105 	if(fid->uid != nil){
106 		vtfree(fid->uid);
107 		fid->uid = nil;
108 	}
109 	if(fid->uname != nil){
110 		vtfree(fid->uname);
111 		fid->uname = nil;
112 	}
113 	if(fid->excl != nil)
114 		exclFree(fid);
115 	if(fid->rpc != nil){
116 		close(fid->rpc->afd);
117 		auth_freerpc(fid->rpc);
118 		fid->rpc = nil;
119 	}
120 	if(fid->fsys != nil){
121 		fsysPut(fid->fsys);
122 		fid->fsys = nil;
123 	}
124 	if(fid->cuname != nil){
125 		vtfree(fid->cuname);
126 		fid->cuname = nil;
127 	}
128 
129 	qlock(&fbox.lock);
130 	fbox.inuse--;
131 	if(fbox.nfree < 10){
132 		fid->hash = fbox.free;
133 		fbox.free = fid;
134 		fbox.nfree++;
135 	}
136 	else{
137 		vtfree(fid);
138 	}
139 	qunlock(&fbox.lock);
140 }
141 
142 static void
fidUnHash(Fid * fid)143 fidUnHash(Fid* fid)
144 {
145 	Fid *fp, **hash;
146 
147 	assert(fid->ref == 0);
148 
149 	hash = &fid->con->fidhash[fid->fidno % NFidHash];
150 	for(fp = *hash; fp != nil; fp = fp->hash){
151 		if(fp == fid){
152 			*hash = fp->hash;
153 			break;
154 		}
155 		hash = &fp->hash;
156 	}
157 	assert(fp == fid);
158 
159 	if(fid->prev != nil)
160 		fid->prev->next = fid->next;
161 	else
162 		fid->con->fhead = fid->next;
163 	if(fid->next != nil)
164 		fid->next->prev = fid->prev;
165 	else
166 		fid->con->ftail = fid->prev;
167 	fid->prev = fid->next = nil;
168 
169 	fid->con->nfid--;
170 }
171 
172 Fid*
fidGet(Con * con,u32int fidno,int flags)173 fidGet(Con* con, u32int fidno, int flags)
174 {
175 	Fid *fid, **hash;
176 
177 	if(fidno == NOFID)
178 		return nil;
179 
180 	hash = &con->fidhash[fidno % NFidHash];
181 	qlock(&con->fidlock);
182 	for(fid = *hash; fid != nil; fid = fid->hash){
183 		if(fid->fidno != fidno)
184 			continue;
185 
186 		/*
187 		 * Already in use is an error
188 		 * when called from attach, clone or walk.
189 		 */
190 		if(flags & FidFCreate){
191 			qunlock(&con->fidlock);
192 			werrstr("%s: fid 0x%ud in use", argv0, fidno);
193 			return nil;
194 		}
195 		fid->ref++;
196 		qunlock(&con->fidlock);
197 
198 		fidLock(fid, flags);
199 		if((fid->open & FidOCreate) || fid->fidno == NOFID){
200 			fidPut(fid);
201 			werrstr("%s: fid invalid", argv0);
202 			return nil;
203 		}
204 		return fid;
205 	}
206 
207 	if((flags & FidFCreate) && (fid = fidAlloc()) != nil){
208 		assert(flags & FidFWlock);
209 		fid->con = con;
210 		fid->fidno = fidno;
211 		fid->ref = 1;
212 
213 		fid->hash = *hash;
214 		*hash = fid;
215 		if(con->ftail != nil){
216 			fid->prev = con->ftail;
217 			con->ftail->next = fid;
218 		}
219 		else{
220 			con->fhead = fid;
221 			fid->prev = nil;
222 		}
223 		con->ftail = fid;
224 		fid->next = nil;
225 
226 		con->nfid++;
227 		qunlock(&con->fidlock);
228 
229 		/*
230 		 * The FidOCreate flag is used to prevent any
231 		 * accidental access to the Fid between unlocking the
232 		 * hash and acquiring the Fid lock for return.
233 		 */
234 		fidLock(fid, flags);
235 		fid->open &= ~FidOCreate;
236 		return fid;
237 	}
238 	qunlock(&con->fidlock);
239 
240 	werrstr("%s: fid not found", argv0);
241 	return nil;
242 }
243 
244 void
fidPut(Fid * fid)245 fidPut(Fid* fid)
246 {
247 	qlock(&fid->con->fidlock);
248 	assert(fid->ref > 0);
249 	fid->ref--;
250 	qunlock(&fid->con->fidlock);
251 
252 	if(fid->ref == 0 && fid->fidno == NOFID){
253 		fidFree(fid);
254 		return;
255 	}
256 	fidUnlock(fid);
257 }
258 
259 void
fidClunk(Fid * fid)260 fidClunk(Fid* fid)
261 {
262 	assert(fid->flags & FidFWlock);
263 
264 	qlock(&fid->con->fidlock);
265 	assert(fid->ref > 0);
266 	fid->ref--;
267 	fidUnHash(fid);
268 	fid->fidno = NOFID;
269 	qunlock(&fid->con->fidlock);
270 
271 	if(fid->ref > 0){
272 		/* not reached - fidUnHash requires ref == 0 */
273 		fidUnlock(fid);
274 		return;
275 	}
276 	fidFree(fid);
277 }
278 
279 void
fidClunkAll(Con * con)280 fidClunkAll(Con* con)
281 {
282 	Fid *fid;
283 	u32int fidno;
284 
285 	qlock(&con->fidlock);
286 	while(con->fhead != nil){
287 		fidno = con->fhead->fidno;
288 		qunlock(&con->fidlock);
289 		if((fid = fidGet(con, fidno, FidFWlock)) != nil)
290 			fidClunk(fid);
291 		qlock(&con->fidlock);
292 	}
293 	qunlock(&con->fidlock);
294 }
295 
296 void
fidInit(void)297 fidInit(void)
298 {
299 }
300