xref: /plan9/sys/src/cmd/cfs/disk.c (revision 7c70c028d2d46a27a61ae88e6df0eb0935d9da7a)
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 
8 int	icformat(Disk*, ulong);
9 
10 /*
11  *  read in the disk structures,  return -1 if the format
12  *  is inconsistent.
13  */
14 int
dinit(Disk * d,int f,int psize,char * expname)15 dinit(Disk *d, int f, int psize, char *expname)
16 {
17 	ulong	i;
18 	uvlong	length;
19 	char	buf[1024];
20 	Bbuf	*b;
21 	Dalloc	*ba;
22 	Dir	*dir;
23 
24 	/*
25 	 *  get disk size
26 	 */
27 	dir = dirfstat(f);
28 	if(dir == nil){
29 		perror("dinit: stat");
30 		return -1;
31 	}
32 	length = dir->length;
33 	free(dir);
34 
35 	/*
36 	 *  read first physical block to get logical block size, # of inodes,
37 	 *  and # of allocation blocks
38 	 */
39 	if(seek(f, 0, 0) < 0){
40 		perror("dinit: seek");
41 		return -1;
42 	}
43 	if(read(f, buf, sizeof(buf)) != sizeof(buf)){
44 		perror("dinit: read");
45 		return -1;
46 	}
47 	ba = (Dalloc*)buf;
48 	if(ba->bsize <= 0){
49 		fprint(2, "dinit: bsize 0x%lux<= 0\n", ba->bsize);
50 		return -1;
51 	}
52 	if((ba->bsize % psize) != 0){
53 		fprint(2, "dinit: logical bsize (%lud) not multiple of physical (%ud)\n",
54 			ba->bsize, psize);
55 		return -1;
56 	}
57 	d->bsize = ba->bsize;
58 	d->nb = length/d->bsize;
59 	d->b2b = (d->bsize - sizeof(Dahdr))*8;
60 	d->nab = (d->nb+d->b2b-1)/d->b2b;
61 	d->p2b = d->bsize/sizeof(Dptr);
62 	strncpy(d->name, ba->name, sizeof d->name);
63 
64 	if (expname != nil && strncmp(d->name, expname, sizeof d->name) != 0) {
65 		/* Mismatch with recorded name; fail here to force a format */
66 		fprint(2, "cfs: name mismatch\n");
67 		return -1;
68 	}
69 
70 	/*
71 	 *  check allocation blocks for consistency
72 	 */
73 	if(bcinit(d, f, d->bsize) < 0){
74 		fprint(2, "dinit: couldn't init block cache\n");
75 		return -1;
76 	}
77 	for(i = 0; i < d->nab; i++){
78 		b = bcread(d, i);
79 		if(b == 0){
80 			perror("dinit: read");
81 			return -1;
82 		}
83 		ba = (Dalloc*)b->data;
84 		if(ba->magic != Amagic){
85 			fprint(2, "dinit: bad magic in alloc block %uld\n", i);
86 			return -1;
87 		}
88 		if(d->bsize != ba->bsize){
89 			fprint(2, "dinit: bad bsize in alloc block %uld\n", i);
90 			return -1;
91 		}
92 		if(d->nab != ba->nab){
93 			fprint(2, "dinit: bad nab in alloc block %uld\n", i);
94 			return -1;
95 		}
96 		if(strncmp(d->name, ba->name, sizeof(d->name))){
97 			fprint(2, "dinit: bad name in alloc block %uld\n", i);
98 			return -1;
99 		}
100 	}
101 	return 0;
102 }
103 
104 /*
105  *  format the disk as a cache
106  */
107 int
dformat(Disk * d,int f,char * name,ulong bsize,ulong psize)108 dformat(Disk *d, int f, char *name, ulong bsize, ulong psize)
109 {
110 	int	i;
111 	uvlong	length;
112 	Bbuf	*b;
113 	Dalloc	*ba;
114 	Dir	*dir;
115 	Dptr	dptr;
116 
117 	fprint(2, "formatting disk\n");
118 
119 	/*
120 	 *  calculate basic numbers
121 	 */
122 	dir = dirfstat(f);
123 	if(dir == nil)
124 		return -1;
125 	length = dir->length;
126 	d->bsize = bsize;
127 	if((d->bsize % psize) != 0){
128 		fprint(2, "cfs: logical bsize not multiple of physical\n");
129 		return -1;
130 	}
131 	d->nb = length/d->bsize;
132 	d->b2b = (d->bsize - sizeof(Dahdr))*8;
133 	d->nab = (d->nb+d->b2b-1)/d->b2b;
134 	d->p2b = d->bsize/sizeof(Dptr);
135 
136 	/*
137 	 *  init allocation blocks
138 	 */
139 	if(bcinit(d, f, d->bsize) < 0)
140 		return -1;
141 	for(i = 0; i < d->nab; i++){
142 		b = bcalloc(d, i);
143 		if(b == 0){
144 			perror("cfs: bcalloc");
145 			return -1;
146 		}
147 		memset(b->data, 0, d->bsize);
148 		ba = (Dalloc*)b->data;
149 		ba->magic = Amagic;
150 		ba->bsize = d->bsize;
151 		ba->nab = d->nab;
152 		strncpy(ba->name, name, sizeof(ba->name));
153 		bcmark(d, b);
154 	}
155 
156 	/*
157 	 *  allocate allocation blocks
158 	 */
159 	for(i = 0; i < d->nab; i++)
160 		if(dalloc(d, &dptr) == Notabno){
161 			fprint(2, "can't allocate allocation blocks\n");
162 			return -1;
163 		}
164 
165 	return bcsync(d);
166 }
167 
168 /*
169  *  allocate a block from a bit vector page
170  *
171  *  a return value of Notabno means no blocks left
172  */
173 static ulong
_balloc(Dalloc * ba,ulong max)174 _balloc(Dalloc *ba, ulong max)
175 {
176 	int len;	/* number of valid words */
177 	ulong i;	/* bit position in long */
178 	ulong m;	/* 1<<i */
179 	ulong v;	/* old value of long */
180 	ulong *p, *e;
181 
182 	/*
183 	 *  find a word with a 0 bit
184 	 */
185 	len = (max+BtoUL-1)/BtoUL;
186 	for(p = ba->bits, e = p + len; p < e; p++)
187 		if(*p != 0xFFFFFFFF)
188 			break;
189 	if(p == e)
190 		return Notabno;
191 
192 	/*
193 	 *  find the first 0 bit
194 	 */
195 	v = *p;
196 	for(m = 1, i = 0; i < BtoUL; i++, m <<= 1)
197 		if((m|v) != v)
198 			break;
199 
200 	/*
201 	 *  calculate block number
202 	 */
203 	i += (p - ba->bits)*BtoUL;
204 	if(i >= max)
205 		return Notabno;
206 
207 	/*
208 	 *  set bit to 1
209 	 */
210 	*p = v | m;
211 	return i;
212 }
213 
214 /*
215  *  allocate a block
216  *
217  *  return Notabno if none left
218  */
219 ulong
dalloc(Disk * d,Dptr * p)220 dalloc(Disk *d, Dptr *p)
221 {
222 	ulong	bno, max, rv;
223 	Bbuf	*b;
224 	Dalloc	*ba;
225 
226 	max = d->nb;
227 	for(bno = 0; bno < d->nab; bno++){
228 		b = bcread(d, bno);
229 		ba = (Dalloc*)b->data;
230 		rv = _balloc(ba, max > d->b2b ? d->b2b : max);
231 		if(rv != Notabno){
232 			rv = bno*d->b2b + rv;
233 			if(p){
234 				p->start = p->end = 0;
235 				p->bno = rv;
236 			}
237 			bcmark(d, b);
238 			return rv;
239 		}
240 		max -= d->b2b;
241 	}
242 	if(p)
243 		p->bno = Notabno;
244 	return Notabno;
245 }
246 
247 /*
248  *  allocate a block of pointers
249  */
250 ulong
dpalloc(Disk * d,Dptr * p)251 dpalloc(Disk *d, Dptr *p)
252 {
253 	Bbuf *b;
254 	Dptr *sp, *ep;
255 
256 	if(dalloc(d, p) == Notabno)
257 		return Notabno;
258 
259 	/*
260 	 *  allocate the page and invalidate all the
261 	 *  pointers
262 	 */
263 	b = bcalloc(d, p->bno);
264 	if(b == 0)
265 		return -1;
266 	sp = (Dptr*)b->data;
267 	for(ep = sp + d->p2b; sp < ep; sp++){
268 		sp->bno = Notabno;
269 		sp->start = sp->end = 0;
270 	}
271 	p->bno |= Indbno;
272 	p->start = 0;
273 	p->end = d->bsize;
274 
275 	/*
276 	 *  mark the page as dirty
277 	 */
278 	bcmark(d, b);
279 	return 0;
280 }
281 
282 /*
283  *  free a block
284  */
285 int
_bfree(Disk * d,ulong i)286 _bfree(Disk *d, ulong i)
287 {
288 	ulong bno, m;
289 	ulong *p;
290 	Bbuf *b;
291 	Dalloc *ba;
292 
293 	/*
294 	 *  get correct allocation block
295 	 */
296 	bno = i/d->b2b;
297 	if(bno >= d->nab)
298 		return -1;
299 	b = bcread(d, bno);
300 	if(b == 0)
301 		return -1;
302 	ba = (Dalloc*)b->data;
303 
304 	/*
305 	 *  change bit
306 	 */
307 	i -= bno*d->b2b;
308 	p = ba->bits + (i/BtoUL);
309 	m = 1<<(i%BtoUL);
310 	*p &= ~m;
311 	bcmark(d, b);
312 
313 	return 0;
314 }
315 
316 /*
317  *  free a block (or blocks)
318  */
319 int
dfree(Disk * d,Dptr * dp)320 dfree(Disk *d, Dptr *dp)
321 {
322 	ulong bno;
323 	Dptr *sp, *ep;
324 	Bbuf *b;
325 
326 	bno = dp->bno;
327 	dp->bno = Notabno;
328 
329 	/*
330 	 *  nothing to free
331 	 */
332 	if(bno == Notabno)
333 		return 0;
334 
335 	/*
336 	 *  direct pointer
337 	 */
338 	if((bno & Indbno) == 0)
339 		return _bfree(d, bno);
340 
341 	/*
342 	 *  first indirect page
343 	 */
344 	bno &= ~Indbno;
345 	_bfree(d, bno);
346 
347 	/*
348 	 *  then all the pages it points to
349 	 *
350 	 *	DANGER: this algorithm may fail if there are more
351 	 *		allocation blocks than block buffers
352 	 */
353 	b = bcread(d, bno);
354 	if(b == 0)
355 		return -1;
356 	sp = (Dptr*)b->data;
357 	for(ep = sp + d->p2b; sp < ep; sp++)
358 		if(dfree(d, sp) < 0)
359 			return -1;
360 	return 0;
361 }
362