xref: /plan9-contrib/sys/src/cmd/ramcfs/inode.c (revision 206fef1c8a79725587ecb1892b58204f8235c098)
1 #include <u.h>
2 #include <libc.h>
3 #include "cformat.h"
4 #include "lru.h"
5 #include "bcache.h"
6 #include "disk.h"
7 #include "inode.h"
8 #include "stats.h"
9 
10 extern int readonly;
11 
12 /*
13  *  read the inode blocks and make sure they
14  *  haven't been trashed.
15  *
16  *  make the in-core table of qid to inode mappings.
17  *	N.B. this is just an array. we need a linear search to find
18  *	     a particular inode. this could be done faster.
19  *
20  *  nab is the first inode block.
21  */
22 int
iinit(Icache * ic,char * mc,int psize,char * name)23 iinit(Icache *ic, char *mc, int psize, char* name)
24 {
25 	Ibuf *b;
26 	Imap *m;
27 	ulong ino;
28 	Bbuf *bb;
29 	Dinode *bi;
30 
31 	/*
32 	 *  get basic sizes and allocation info from disk
33 	 */
34 	if(dinit(ic, mc, psize, name) < 0)
35 		return -1;
36 
37 	/*
38 	 *  read first inode block to get number of inodes
39 	 */
40 	bb = bcread(ic, ic->nab);
41 	if(bb == 0){
42 		fprint(2, "iinit: can't read disk\n");
43 		return -1;
44 	}
45 	bi = (Dinode*)bb->data;
46 	if(bi->nino==0 || bi->nino>2048){
47 		fprint(2, "iinit: bad nino\n");
48 		return -1;
49 	}
50 	ic->nino = bi->nino;
51 
52 	/*
53 	 *  set up sizing constants
54 	 */
55 	ic->i2b = (ic->bsize - sizeof(Dihdr))/sizeof(Inode);
56 	ic->nib = (ic->nino + ic->i2b - 1)/ic->i2b;
57 
58 	/*
59 	 *  allocate the in-core qid/inode map, build it's lru
60 	 */
61 	if(ic->map)
62 		free(ic->map);
63 	ic->map = malloc(sizeof(Imap)*ic->nino);
64 	if(ic->map == 0){
65 		fprint(2, "iinit: can't alloc map\n");
66 		return -1;
67 	}
68 	lruinit(&ic->mlru);
69 	for(m = ic->map; m < &ic->map[ic->nino]; m++){
70 		m->inuse = 0;
71 		m->b = 0;
72 		lruadd(&ic->mlru, m);
73 	}
74 
75 	/*
76 	 *  mark all cache buffers as empty, put them on the lru list
77 	 */
78 	lruinit(&ic->blru);
79 	for(b = ic->ib; b < &ic->ib[Nicache]; b++){
80 		b->inuse = 0;
81 		lruadd(&ic->blru, b);
82 	}
83 
84 	/*
85 	 *  Read all inodes and
86 	 *  build the in-core qid/inode map
87 	 */
88 	for(ino = 0; ino < ic->nino; ino++){
89 		b = iread(ic, ino);
90 		if(b == 0){
91 			fprint(2, "iinit: can't read inode %ld\n", ino);
92 			return -1;
93 		}
94 		if(b->inode.inuse){
95 			m = &ic->map[ino];
96 			m->inuse = 1;
97 			m->qid = b->inode.qid;
98 			lruref(&ic->mlru, m);
99 		}
100 	}
101 	return 0;
102 }
103 
104 /*
105  *  format the inode blocks
106  */
107 int
iformat(Icache * ic,char * mc,ulong nino,char * name,int bsize,int psize)108 iformat(Icache *ic, char *mc, ulong nino, char *name, int bsize, int psize)
109 {
110 	int nib;
111 	ulong bno, i2b, i;
112 	Bbuf *bb;
113 	Dinode *bi;
114 
115 	/*
116 	 *  first format disk allocation
117 	 */
118 	if(dformat(ic, mc, name, bsize, psize) < 0)
119 		return -1;
120 
121 	fprint(2, "formatting inodes in memory\n");
122 
123 	i2b = (bsize - sizeof(Dihdr))/sizeof(Inode);
124 	nib = (nino + i2b - 1)/i2b;
125 
126 	for(bno = ic->nab; bno < ic->nab + nib; bno++){
127 		if(dalloc(ic, 0) == Notabno){
128 			fprint(2, "iformat: balloc failed\n");
129 			return -1;
130 		}
131 		bb = bcalloc(ic, bno);
132 		if(bb == 0){
133 			fprint(2, "iformat: bcalloc failed\n");
134 			return -1;
135 		}
136 		bi = (Dinode*)bb->data;
137 		bi->magic = Imagic;
138 		bi->nino = nino;
139 		for(i = 0; i < i2b; i++)
140 			bi->inode[i].inuse = 0;
141 		bcmark(ic, bb);
142 	}
143 
144 	bcsync(ic);
145 
146 	return iinit(ic, mc, psize, name);
147 }
148 
149 /*
150  *  allocate a cache buffer, use least recently used
151  */
152 Ibuf*
ialloc(Icache * ic,ulong ino)153 ialloc(Icache *ic, ulong ino)
154 {
155 	Imap *m;
156 	Ibuf *b;
157 
158 	b = (Ibuf*)ic->blru.lnext;
159 	if(b->inuse)
160 		ic->map[b->ino].b = 0;
161 	b->ino = ino;
162 	b->inuse = 1;
163 	m = &ic->map[ino];
164 	m->b = b;
165 	return b;
166 }
167 
168 /*
169  *  free a cache buffer
170  */
171 void
ifree(Icache * ic,Ibuf * b)172 ifree(Icache *ic, Ibuf *b)
173 {
174 	b->inuse = 0;
175 	if(b->inuse)
176 		ic->map[b->ino].b = 0;
177 	lruderef(&ic->blru, b);
178 }
179 
180 /*
181  *  get an inode into the cache.  if no inode exists for this qid, create one
182  *  from an unused qid/inode map.
183  */
184 Ibuf *
iget(Icache * ic,Qid qid)185 iget(Icache *ic, Qid qid)
186 {
187 	Imap *m, *me;
188 	Ibuf *b;
189 
190 	/*
191 	 *  find map entry with same qid.path
192 	 */
193 	for(m = ic->map, me = &ic->map[ic->nino]; m < me; m++)
194 		if(m->inuse && m->qid.path==qid.path){
195 			if((m->qid.vers != qid.vers || qid.vers == 0)
196 			    && !readonly){
197 				/*
198 				 *  our info is old or this is likely
199 				 *  a synthetic file, so forget it
200 				 */
201 				DPRINT(2, "updating old file %llud.%lud\n",
202 					qid.path, qid.vers);
203 				m->qid = qid;
204 				iupdate(ic, m - ic->map, qid);
205 			}
206 			break;
207 		}
208 
209 	/*
210  	 *  if an already existing inode, just get it
211 	 */
212 	if(m != me)
213 		return iread(ic, m - ic->map);
214 
215 	/*
216 	 *  create a new inode, throw out the least recently used inode
217 	 *  if necessary
218 	 */
219 	m = (Imap*)ic->mlru.lnext;
220 	if(m->inuse){
221 		DPRINT(2, "superceding file %llud.%ld by %llud.%ld\n",
222 			m->qid.path, m->qid.vers, qid.path, qid.vers);
223 		if(iremove(ic, m - ic->map) < 0)
224 			return 0;
225 	}
226 
227 	if(statson)
228 		cfsstat.ninsert++;
229 	/*
230 	 *  init inode and write to disk
231 	 */
232 	DPRINT(2, "new file %llud.%ld ino %ld\n",
233 		qid.path, qid.vers, m - ic->map);
234 	b = ialloc(ic, m - ic->map);
235 	b->inode.inuse = m->inuse = 1;
236 	b->inode.qid = qid;
237 	b->inode.length = 0x7fffffffffffffffLL;
238 	m->qid = qid;
239 	b->inode.ptr.bno = Notabno;
240 	iwrite(ic, b);
241 	return b;
242 }
243 
244 /*
245  *  read an inode into the cache
246  *
247  *  ASSUMPTION: the inode is valid
248  */
249 Ibuf*
iread(Icache * ic,ulong ino)250 iread(Icache *ic, ulong ino)
251 {
252 	Ibuf *b;
253 	Imap *m;
254 	ulong bno;
255 	Bbuf *bb;
256 	Dinode *bi;
257 
258 	/*
259 	 *  first see if we already have it in a cache entry
260 	 */
261 	m = &ic->map[ino];
262 	if(m->inuse && m->b){
263 		b = m->b;
264 		goto out;
265 	}
266 
267 	/*
268 	 *  read it
269 	 */
270 	b = ialloc(ic, ino);
271 	bno = ic->nab + ino/ic->i2b;
272 	bb = bcread(ic, bno);
273 	if(bb == 0){
274 		ifree(ic, b);
275 		return 0;
276 	}
277 	bi = (Dinode*)bb->data;
278 	b->inode = bi->inode[ino % ic->i2b];
279 
280 	/*
281 	 *  consistency check
282 	 */
283 	if(bi->nino!=ic->nino || bi->magic!=Imagic){
284 		fprint(2, "iread: inconsistent inode block\n");
285 		ifree(ic, b);
286 		return 0;
287 	}
288 out:
289 	b->inuse = 1;
290 	m->b = b;
291 	if(b->inode.inuse)
292 		lruref(&ic->mlru, m);
293 	lruref(&ic->blru, b);
294 	return b;
295 }
296 
297 /*
298  *  write an inode back to disk
299  */
300 int
iwrite(Icache * ic,Ibuf * b)301 iwrite(Icache *ic, Ibuf *b)
302 {
303 	ulong bno;
304 	Bbuf *bb;
305 	Dinode *bi;
306 
307 	bno = ic->nab + b->ino/ic->i2b;
308 	bb = bcread(ic, bno);
309 	if(bb == 0)
310 		return 0;
311 	bi = (Dinode*)bb->data;
312 	bi->inode[b->ino % ic->i2b] = b->inode;
313 	bcmark(ic, bb);
314 	lruref(&ic->mlru, &ic->map[b->ino]);
315 	lruref(&ic->blru, b);
316 	return 0;
317 }
318 
319 /*
320  *  Forget what we know about an inode without removing it
321  *
322  *	N.B: ordering of iwrite and dfree is important
323  */
324 int
iupdate(Icache * ic,ulong ino,Qid qid)325 iupdate(Icache *ic, ulong ino, Qid qid)
326 {
327 	Ibuf *b;
328 	Imap *m;
329 	Dptr d;
330 
331 	if(statson)
332 		cfsstat.nupdate++;
333 	b = iread(ic, ino);
334 	if(b == 0)
335 		return -1;
336 
337 	/*
338 	 *  update inode and map
339 	 */
340 	b->inode.qid = qid;
341 	b->inode.length = 0x7fffffffffffffffLL;	/* Set to maximum */
342 	m = &ic->map[ino];
343 	m->qid = qid;
344 
345 	/*
346 	 *  the free is not done if the write fails!
347 	 *  this is important
348 	 */
349 	d = b->inode.ptr;
350 	b->inode.ptr.bno = Notabno;
351 	if(iwrite(ic, b) < 0)
352 		return -1;
353 	dfree(ic, &d);
354 	return 0;
355 }
356 
357 /*
358  *  remove an inode
359  *
360  *	N.B: ordering of iwrite and dfree is important
361  */
362 int
iremove(Icache * ic,ulong ino)363 iremove(Icache *ic, ulong ino)
364 {
365 	Ibuf *b;
366 	Imap *m;
367 
368 	if(statson)
369 		cfsstat.ndelete++;
370 	m = &ic->map[ino];
371 
372 	/*
373 	 *  read in inode
374 	 */
375 	b = iread(ic, ino);
376 	if(b == 0)
377 		return -1;
378 
379 	/*
380 	 *  mark it unused on disk
381 	 */
382 	b->inode.inuse = 0;
383 	if(iwrite(ic, b) < 0)
384 		return -1;
385 
386 	/*
387 	 *  throw out it's data pages
388 	 */
389 	dfree(ic, &b->inode.ptr);
390 
391 	/*
392 	 *  free the inode buffer
393 	 */
394 	ifree(ic, b);
395 
396 	/*
397 	 *  make map entry least recently used
398 	 */
399 	lruderef(&ic->mlru, m);
400 	return 0;
401 }
402 
403 /*
404  *  increment our version number
405  */
406 void
iinc(Icache * ic,Ibuf * b)407 iinc(Icache *ic, Ibuf *b)
408 {
409 	b->inode.qid.vers++;
410 	ic->map[b->ino].qid = b->inode.qid;
411 	iwrite(ic, b);
412 }
413