1 |
|
|
/* $OpenBSD: inode.c,v 1.47 2015/12/10 17:26:59 mmcc Exp $ */ |
2 |
|
|
/* $NetBSD: inode.c,v 1.23 1996/10/11 20:15:47 thorpej Exp $ */ |
3 |
|
|
|
4 |
|
|
/* |
5 |
|
|
* Copyright (c) 1980, 1986, 1993 |
6 |
|
|
* The Regents of the University of California. All rights reserved. |
7 |
|
|
* |
8 |
|
|
* Redistribution and use in source and binary forms, with or without |
9 |
|
|
* modification, are permitted provided that the following conditions |
10 |
|
|
* are met: |
11 |
|
|
* 1. Redistributions of source code must retain the above copyright |
12 |
|
|
* notice, this list of conditions and the following disclaimer. |
13 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
14 |
|
|
* notice, this list of conditions and the following disclaimer in the |
15 |
|
|
* documentation and/or other materials provided with the distribution. |
16 |
|
|
* 3. Neither the name of the University nor the names of its contributors |
17 |
|
|
* may be used to endorse or promote products derived from this software |
18 |
|
|
* without specific prior written permission. |
19 |
|
|
* |
20 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
21 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
22 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
23 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
24 |
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
25 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
26 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
27 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
28 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
29 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
30 |
|
|
* SUCH DAMAGE. |
31 |
|
|
*/ |
32 |
|
|
|
33 |
|
|
#include <sys/param.h> /* setbit btodb */ |
34 |
|
|
#include <sys/time.h> |
35 |
|
|
#include <ufs/ufs/dinode.h> |
36 |
|
|
#include <ufs/ufs/dir.h> |
37 |
|
|
#include <ufs/ffs/fs.h> |
38 |
|
|
#ifndef SMALL |
39 |
|
|
#include <pwd.h> |
40 |
|
|
#endif |
41 |
|
|
#include <stdio.h> |
42 |
|
|
#include <stdlib.h> |
43 |
|
|
#include <string.h> |
44 |
|
|
#include <unistd.h> |
45 |
|
|
#include <limits.h> |
46 |
|
|
|
47 |
|
|
#include "fsck.h" |
48 |
|
|
#include "fsutil.h" |
49 |
|
|
#include "extern.h" |
50 |
|
|
|
51 |
|
|
#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) |
52 |
|
|
#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) |
53 |
|
|
|
54 |
|
|
static ino_t startinum; |
55 |
|
|
|
56 |
|
|
static int iblock(struct inodesc *, long, off_t); |
57 |
|
|
|
58 |
|
|
int |
59 |
|
|
ckinode(union dinode *dp, struct inodesc *idesc) |
60 |
|
|
{ |
61 |
|
|
long ret, ndb, offset; |
62 |
|
|
union dinode dino; |
63 |
|
|
off_t sizepb, remsize; |
64 |
|
|
mode_t mode; |
65 |
|
|
int i; |
66 |
|
|
char pathbuf[PATH_MAX + 1]; |
67 |
|
|
|
68 |
|
|
if (idesc->id_fix != IGNORE) |
69 |
|
|
idesc->id_fix = DONTKNOW; |
70 |
|
|
idesc->id_entryno = 0; |
71 |
|
|
idesc->id_filesize = DIP(dp, di_size); |
72 |
|
|
mode = DIP(dp, di_mode) & IFMT; |
73 |
|
|
if (mode == IFBLK || mode == IFCHR || (mode == IFLNK && |
74 |
|
|
(DIP(dp, di_size) < sblock.fs_maxsymlinklen || |
75 |
|
|
(sblock.fs_maxsymlinklen == 0 && DIP(dp, di_blocks) == 0)))) |
76 |
|
|
return (KEEPON); |
77 |
|
|
if (sblock.fs_magic == FS_UFS1_MAGIC) |
78 |
|
|
dino.dp1 = dp->dp1; |
79 |
|
|
else |
80 |
|
|
dino.dp2 = dp->dp2; |
81 |
|
|
ndb = howmany(DIP(&dino, di_size), sblock.fs_bsize); |
82 |
|
|
for (i = 0; i < NDADDR; i++) { |
83 |
|
|
if (--ndb == 0 && (offset = blkoff(&sblock, |
84 |
|
|
DIP(&dino, di_size))) != 0) |
85 |
|
|
idesc->id_numfrags = |
86 |
|
|
numfrags(&sblock, fragroundup(&sblock, offset)); |
87 |
|
|
else |
88 |
|
|
idesc->id_numfrags = sblock.fs_frag; |
89 |
|
|
if (DIP(&dino, di_db[i]) == 0) { |
90 |
|
|
if (idesc->id_type == DATA && ndb >= 0) { |
91 |
|
|
/* An empty block in a directory XXX */ |
92 |
|
|
getpathname(pathbuf, sizeof pathbuf, |
93 |
|
|
idesc->id_number, idesc->id_number); |
94 |
|
|
pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS", |
95 |
|
|
pathbuf); |
96 |
|
|
if (reply("ADJUST LENGTH") == 1) { |
97 |
|
|
dp = ginode(idesc->id_number); |
98 |
|
|
DIP_SET(dp, di_size, |
99 |
|
|
i * sblock.fs_bsize); |
100 |
|
|
printf( |
101 |
|
|
"YOU MUST RERUN FSCK AFTERWARDS\n"); |
102 |
|
|
rerun = 1; |
103 |
|
|
inodirty(); |
104 |
|
|
} |
105 |
|
|
} |
106 |
|
|
continue; |
107 |
|
|
} |
108 |
|
|
idesc->id_blkno = DIP(&dino, di_db[i]); |
109 |
|
|
if (idesc->id_type == ADDR) |
110 |
|
|
ret = (*idesc->id_func)(idesc); |
111 |
|
|
else |
112 |
|
|
ret = dirscan(idesc); |
113 |
|
|
if (ret & STOP) |
114 |
|
|
return (ret); |
115 |
|
|
} |
116 |
|
|
idesc->id_numfrags = sblock.fs_frag; |
117 |
|
|
remsize = DIP(&dino, di_size) - sblock.fs_bsize * NDADDR; |
118 |
|
|
sizepb = sblock.fs_bsize; |
119 |
|
|
for (i = 0; i < NIADDR; i++) { |
120 |
|
|
if (DIP(&dino, di_ib[i])) { |
121 |
|
|
idesc->id_blkno = DIP(&dino, di_ib[i]); |
122 |
|
|
ret = iblock(idesc, i + 1, remsize); |
123 |
|
|
if (ret & STOP) |
124 |
|
|
return (ret); |
125 |
|
|
} else { |
126 |
|
|
if (idesc->id_type == DATA && remsize > 0) { |
127 |
|
|
/* An empty block in a directory XXX */ |
128 |
|
|
getpathname(pathbuf, sizeof pathbuf, |
129 |
|
|
idesc->id_number, idesc->id_number); |
130 |
|
|
pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS", |
131 |
|
|
pathbuf); |
132 |
|
|
if (reply("ADJUST LENGTH") == 1) { |
133 |
|
|
dp = ginode(idesc->id_number); |
134 |
|
|
DIP_SET(dp, di_size, |
135 |
|
|
DIP(dp, di_size) - remsize); |
136 |
|
|
remsize = 0; |
137 |
|
|
printf( |
138 |
|
|
"YOU MUST RERUN FSCK AFTERWARDS\n"); |
139 |
|
|
rerun = 1; |
140 |
|
|
inodirty(); |
141 |
|
|
break; |
142 |
|
|
} |
143 |
|
|
} |
144 |
|
|
} |
145 |
|
|
sizepb *= NINDIR(&sblock); |
146 |
|
|
remsize -= sizepb; |
147 |
|
|
} |
148 |
|
|
return (KEEPON); |
149 |
|
|
} |
150 |
|
|
|
151 |
|
|
static int |
152 |
|
|
iblock(struct inodesc *idesc, long ilevel, off_t isize) |
153 |
|
|
{ |
154 |
|
|
struct bufarea *bp; |
155 |
|
|
int i, n, (*func)(struct inodesc *), nif; |
156 |
|
|
off_t sizepb; |
157 |
|
|
char buf[BUFSIZ]; |
158 |
|
|
char pathbuf[PATH_MAX + 1]; |
159 |
|
|
union dinode *dp; |
160 |
|
|
|
161 |
|
|
if (idesc->id_type == ADDR) { |
162 |
|
|
func = idesc->id_func; |
163 |
|
|
if (((n = (*func)(idesc)) & KEEPON) == 0) |
164 |
|
|
return (n); |
165 |
|
|
} else |
166 |
|
|
func = dirscan; |
167 |
|
|
if (isize < 0 || chkrange(idesc->id_blkno, idesc->id_numfrags)) |
168 |
|
|
return (SKIP); |
169 |
|
|
bp = getdatablk(idesc->id_blkno, sblock.fs_bsize); |
170 |
|
|
ilevel--; |
171 |
|
|
for (sizepb = sblock.fs_bsize, i = 0; i < ilevel; i++) |
172 |
|
|
sizepb *= NINDIR(&sblock); |
173 |
|
|
if (howmany(isize, sizepb) > NINDIR(&sblock)) |
174 |
|
|
nif = NINDIR(&sblock); |
175 |
|
|
else |
176 |
|
|
nif = howmany(isize, sizepb); |
177 |
|
|
if (idesc->id_func == pass1check && nif < NINDIR(&sblock)) { |
178 |
|
|
for (i = nif; i < NINDIR(&sblock); i++) { |
179 |
|
|
if (IBLK(bp, i) == 0) |
180 |
|
|
continue; |
181 |
|
|
(void)snprintf(buf, sizeof buf, |
182 |
|
|
"PARTIALLY TRUNCATED INODE I=%llu", |
183 |
|
|
(unsigned long long)idesc->id_number); |
184 |
|
|
if (preen) |
185 |
|
|
pfatal("%s", buf); |
186 |
|
|
else if (dofix(idesc, buf)) { |
187 |
|
|
IBLK_SET(bp, i, 0); |
188 |
|
|
dirty(bp); |
189 |
|
|
} |
190 |
|
|
} |
191 |
|
|
flush(fswritefd, bp); |
192 |
|
|
} |
193 |
|
|
for (i = 0; i < nif; i++) { |
194 |
|
|
if (IBLK(bp, i)) { |
195 |
|
|
idesc->id_blkno = IBLK(bp, i); |
196 |
|
|
if (ilevel == 0) |
197 |
|
|
n = (*func)(idesc); |
198 |
|
|
else |
199 |
|
|
n = iblock(idesc, ilevel, isize); |
200 |
|
|
if (n & STOP) { |
201 |
|
|
bp->b_flags &= ~B_INUSE; |
202 |
|
|
return (n); |
203 |
|
|
} |
204 |
|
|
} else { |
205 |
|
|
if (idesc->id_type == DATA && isize > 0) { |
206 |
|
|
/* An empty block in a directory XXX */ |
207 |
|
|
getpathname(pathbuf, sizeof pathbuf, |
208 |
|
|
idesc->id_number, idesc->id_number); |
209 |
|
|
pfatal("DIRECTORY %s: CONTAINS EMPTY BLOCKS", |
210 |
|
|
pathbuf); |
211 |
|
|
if (reply("ADJUST LENGTH") == 1) { |
212 |
|
|
dp = ginode(idesc->id_number); |
213 |
|
|
DIP_SET(dp, di_size, |
214 |
|
|
DIP(dp, di_size) - isize); |
215 |
|
|
isize = 0; |
216 |
|
|
printf( |
217 |
|
|
"YOU MUST RERUN FSCK AFTERWARDS\n"); |
218 |
|
|
rerun = 1; |
219 |
|
|
inodirty(); |
220 |
|
|
bp->b_flags &= ~B_INUSE; |
221 |
|
|
return(STOP); |
222 |
|
|
} |
223 |
|
|
} |
224 |
|
|
} |
225 |
|
|
isize -= sizepb; |
226 |
|
|
} |
227 |
|
|
bp->b_flags &= ~B_INUSE; |
228 |
|
|
return (KEEPON); |
229 |
|
|
} |
230 |
|
|
|
231 |
|
|
/* |
232 |
|
|
* Check that a block in a legal block number. |
233 |
|
|
* Return 0 if in range, 1 if out of range. |
234 |
|
|
*/ |
235 |
|
|
int |
236 |
|
|
chkrange(daddr_t blk, int cnt) |
237 |
|
|
{ |
238 |
|
|
int c; |
239 |
|
|
|
240 |
|
|
if (cnt <= 0 || blk <= 0 || blk > maxfsblock || |
241 |
|
|
cnt - 1 > maxfsblock - blk) |
242 |
|
|
return (1); |
243 |
|
|
if (cnt > sblock.fs_frag || |
244 |
|
|
fragnum(&sblock, blk) + cnt > sblock.fs_frag) { |
245 |
|
|
if (debug) |
246 |
|
|
printf("bad size: blk %lld, offset %lld, size %d\n", |
247 |
|
|
(long long)blk, (long long)fragnum(&sblock, blk), |
248 |
|
|
cnt); |
249 |
|
|
return (1); |
250 |
|
|
} |
251 |
|
|
c = dtog(&sblock, blk); |
252 |
|
|
if (blk < cgdmin(&sblock, c)) { |
253 |
|
|
if ((blk + cnt) > cgsblock(&sblock, c)) { |
254 |
|
|
if (debug) { |
255 |
|
|
printf("blk %lld < cgdmin %lld;", |
256 |
|
|
(long long)blk, |
257 |
|
|
(long long)cgdmin(&sblock, c)); |
258 |
|
|
printf(" blk + cnt %lld > cgsbase %lld\n", |
259 |
|
|
(long long)(blk + cnt), |
260 |
|
|
(long long)cgsblock(&sblock, c)); |
261 |
|
|
} |
262 |
|
|
return (1); |
263 |
|
|
} |
264 |
|
|
} else { |
265 |
|
|
if ((blk + cnt) > cgbase(&sblock, c+1)) { |
266 |
|
|
if (debug) { |
267 |
|
|
printf("blk %lld >= cgdmin %lld;", |
268 |
|
|
(long long)blk, |
269 |
|
|
(long long)cgdmin(&sblock, c)); |
270 |
|
|
printf(" blk + cnt %lld > sblock.fs_fpg %d\n", |
271 |
|
|
(long long)(blk+cnt), sblock.fs_fpg); |
272 |
|
|
} |
273 |
|
|
return (1); |
274 |
|
|
} |
275 |
|
|
} |
276 |
|
|
return (0); |
277 |
|
|
} |
278 |
|
|
|
279 |
|
|
/* |
280 |
|
|
* General purpose interface for reading inodes. |
281 |
|
|
*/ |
282 |
|
|
union dinode * |
283 |
|
|
ginode(ino_t inumber) |
284 |
|
|
{ |
285 |
|
|
daddr_t iblk; |
286 |
|
|
|
287 |
|
|
if (inumber < ROOTINO || inumber > maxino) |
288 |
|
|
errexit("bad inode number %llu to ginode\n", |
289 |
|
|
(unsigned long long)inumber); |
290 |
|
|
if (startinum == 0 || |
291 |
|
|
inumber < startinum || inumber >= startinum + INOPB(&sblock)) { |
292 |
|
|
iblk = ino_to_fsba(&sblock, inumber); |
293 |
|
|
if (pbp != 0) |
294 |
|
|
pbp->b_flags &= ~B_INUSE; |
295 |
|
|
pbp = getdatablk(iblk, sblock.fs_bsize); |
296 |
|
|
startinum = (inumber / INOPB(&sblock)) * INOPB(&sblock); |
297 |
|
|
} |
298 |
|
|
if (sblock.fs_magic == FS_UFS1_MAGIC) |
299 |
|
|
return ((union dinode *) |
300 |
|
|
&pbp->b_un.b_dinode1[inumber % INOPB(&sblock)]); |
301 |
|
|
return ((union dinode *)&pbp->b_un.b_dinode2[inumber % INOPB(&sblock)]); |
302 |
|
|
} |
303 |
|
|
|
304 |
|
|
/* |
305 |
|
|
* Special purpose version of ginode used to optimize first pass |
306 |
|
|
* over all the inodes in numerical order. |
307 |
|
|
*/ |
308 |
|
|
ino_t nextino, lastinum; |
309 |
|
|
long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize; |
310 |
|
|
static caddr_t inodebuf; |
311 |
|
|
|
312 |
|
|
union dinode * |
313 |
|
|
getnextinode(ino_t inumber) |
314 |
|
|
{ |
315 |
|
|
long size; |
316 |
|
|
daddr_t dblk; |
317 |
|
|
union dinode *dp; |
318 |
|
|
static caddr_t nextinop; |
319 |
|
|
|
320 |
|
|
if (inumber != nextino++ || inumber > maxino) |
321 |
|
|
errexit("bad inode number %llu to nextinode %llu\n", |
322 |
|
|
(unsigned long long)inumber, |
323 |
|
|
(unsigned long long)nextino); |
324 |
|
|
if (inumber >= lastinum) { |
325 |
|
|
readcnt++; |
326 |
|
|
dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum)); |
327 |
|
|
if (readcnt % readpercg == 0) { |
328 |
|
|
size = partialsize; |
329 |
|
|
lastinum += partialcnt; |
330 |
|
|
} else { |
331 |
|
|
size = inobufsize; |
332 |
|
|
lastinum += fullcnt; |
333 |
|
|
} |
334 |
|
|
(void)bread(fsreadfd, inodebuf, dblk, size); |
335 |
|
|
nextinop = inodebuf; |
336 |
|
|
} |
337 |
|
|
dp = (union dinode *)nextinop; |
338 |
|
|
if (sblock.fs_magic == FS_UFS1_MAGIC) |
339 |
|
|
nextinop += sizeof(struct ufs1_dinode); |
340 |
|
|
else |
341 |
|
|
nextinop += sizeof(struct ufs2_dinode); |
342 |
|
|
return (dp); |
343 |
|
|
} |
344 |
|
|
|
345 |
|
|
void |
346 |
|
|
setinodebuf(ino_t inum) |
347 |
|
|
{ |
348 |
|
|
|
349 |
|
|
startinum = 0; |
350 |
|
|
nextino = inum; |
351 |
|
|
lastinum = inum; |
352 |
|
|
readcnt = 0; |
353 |
|
|
if (inodebuf != NULL) |
354 |
|
|
return; |
355 |
|
|
inobufsize = blkroundup(&sblock, INOBUFSIZE); |
356 |
|
|
if (sblock.fs_magic == FS_UFS1_MAGIC) |
357 |
|
|
fullcnt = inobufsize / sizeof(struct ufs1_dinode); |
358 |
|
|
else |
359 |
|
|
fullcnt = inobufsize / sizeof(struct ufs2_dinode); |
360 |
|
|
readpercg = sblock.fs_ipg / fullcnt; |
361 |
|
|
partialcnt = sblock.fs_ipg % fullcnt; |
362 |
|
|
if (sblock.fs_magic == FS_UFS1_MAGIC) |
363 |
|
|
partialsize = partialcnt * sizeof(struct ufs1_dinode); |
364 |
|
|
else |
365 |
|
|
partialsize = partialcnt * sizeof(struct ufs2_dinode); |
366 |
|
|
if (partialcnt != 0) { |
367 |
|
|
readpercg++; |
368 |
|
|
} else { |
369 |
|
|
partialcnt = fullcnt; |
370 |
|
|
partialsize = inobufsize; |
371 |
|
|
} |
372 |
|
|
if (inodebuf == NULL && |
373 |
|
|
(inodebuf = malloc((unsigned)inobufsize)) == NULL) |
374 |
|
|
errexit("Cannot allocate space for inode buffer\n"); |
375 |
|
|
} |
376 |
|
|
|
377 |
|
|
void |
378 |
|
|
freeinodebuf(void) |
379 |
|
|
{ |
380 |
|
|
|
381 |
|
|
free(inodebuf); |
382 |
|
|
inodebuf = NULL; |
383 |
|
|
} |
384 |
|
|
|
385 |
|
|
/* |
386 |
|
|
* Routines to maintain information about directory inodes. |
387 |
|
|
* This is built during the first pass and used during the |
388 |
|
|
* second and third passes. |
389 |
|
|
* |
390 |
|
|
* Enter inodes into the cache. |
391 |
|
|
*/ |
392 |
|
|
void |
393 |
|
|
cacheino(union dinode *dp, ino_t inumber) |
394 |
|
|
{ |
395 |
|
|
struct inoinfo *inp; |
396 |
|
|
struct inoinfo **inpp, **newinpsort; |
397 |
|
|
unsigned int blks; |
398 |
|
|
long newlistmax; |
399 |
|
|
int i; |
400 |
|
|
|
401 |
|
|
blks = howmany(DIP(dp, di_size), sblock.fs_bsize); |
402 |
|
|
if (blks > NDADDR) |
403 |
|
|
blks = NDADDR + NIADDR; |
404 |
|
|
inp = malloc(sizeof(*inp) + (blks ? blks - 1 : 0) * sizeof(daddr_t)); |
405 |
|
|
if (inp == NULL) |
406 |
|
|
errexit("cannot allocate memory for inode cache\n"); |
407 |
|
|
inpp = &inphead[inumber % numdirs]; |
408 |
|
|
inp->i_nexthash = *inpp; |
409 |
|
|
*inpp = inp; |
410 |
|
|
inp->i_child = inp->i_sibling = 0; |
411 |
|
|
if (inumber == ROOTINO) |
412 |
|
|
inp->i_parent = ROOTINO; |
413 |
|
|
else |
414 |
|
|
inp->i_parent = 0; |
415 |
|
|
inp->i_dotdot = 0; |
416 |
|
|
inp->i_number = inumber; |
417 |
|
|
inp->i_isize = DIP(dp, di_size); |
418 |
|
|
inp->i_numblks = blks; |
419 |
|
|
for (i = 0; i < (blks < NDADDR ? blks : NDADDR); i++) |
420 |
|
|
inp->i_blks[i] = DIP(dp, di_db[i]); |
421 |
|
|
if (blks > NDADDR) |
422 |
|
|
for (i = 0; i < NIADDR; i++) |
423 |
|
|
inp->i_blks[NDADDR + i] = DIP(dp, di_ib[i]); |
424 |
|
|
if (inplast == listmax) { |
425 |
|
|
newlistmax = listmax + 100; |
426 |
|
|
newinpsort = reallocarray(inpsort, |
427 |
|
|
(unsigned)newlistmax, sizeof(struct inoinfo *)); |
428 |
|
|
if (newinpsort == NULL) |
429 |
|
|
errexit("cannot increase directory list"); |
430 |
|
|
inpsort = newinpsort; |
431 |
|
|
listmax = newlistmax; |
432 |
|
|
} |
433 |
|
|
inpsort[inplast++] = inp; |
434 |
|
|
} |
435 |
|
|
|
436 |
|
|
/* |
437 |
|
|
* Look up an inode cache structure. |
438 |
|
|
*/ |
439 |
|
|
struct inoinfo * |
440 |
|
|
getinoinfo(ino_t inumber) |
441 |
|
|
{ |
442 |
|
|
struct inoinfo *inp; |
443 |
|
|
|
444 |
|
|
for (inp = inphead[inumber % numdirs]; inp; inp = inp->i_nexthash) { |
445 |
|
|
if (inp->i_number != inumber) |
446 |
|
|
continue; |
447 |
|
|
return (inp); |
448 |
|
|
} |
449 |
|
|
errexit("cannot find inode %llu\n", (unsigned long long)inumber); |
450 |
|
|
return (NULL); |
451 |
|
|
} |
452 |
|
|
|
453 |
|
|
/* |
454 |
|
|
* Clean up all the inode cache structure. |
455 |
|
|
*/ |
456 |
|
|
void |
457 |
|
|
inocleanup(void) |
458 |
|
|
{ |
459 |
|
|
struct inoinfo **inpp; |
460 |
|
|
|
461 |
|
|
if (inphead == NULL) |
462 |
|
|
return; |
463 |
|
|
for (inpp = &inpsort[inplast - 1]; inpp >= inpsort; inpp--) |
464 |
|
|
free(*inpp); |
465 |
|
|
free(inphead); |
466 |
|
|
free(inpsort); |
467 |
|
|
inphead = inpsort = NULL; |
468 |
|
|
} |
469 |
|
|
|
470 |
|
|
void |
471 |
|
|
inodirty(void) |
472 |
|
|
{ |
473 |
|
|
dirty(pbp); |
474 |
|
|
} |
475 |
|
|
|
476 |
|
|
void |
477 |
|
|
clri(struct inodesc *idesc, char *type, int flag) |
478 |
|
|
{ |
479 |
|
|
union dinode *dp; |
480 |
|
|
|
481 |
|
|
dp = ginode(idesc->id_number); |
482 |
|
|
if (flag == 1) { |
483 |
|
|
pwarn("%s %s", type, |
484 |
|
|
(DIP(dp, di_mode) & IFMT) == IFDIR ? "DIR" : "FILE"); |
485 |
|
|
pinode(idesc->id_number); |
486 |
|
|
} |
487 |
|
|
if (preen || reply("CLEAR") == 1) { |
488 |
|
|
if (preen) |
489 |
|
|
printf(" (CLEARED)\n"); |
490 |
|
|
n_files--; |
491 |
|
|
(void)ckinode(dp, idesc); |
492 |
|
|
clearinode(dp); |
493 |
|
|
SET_ISTATE(idesc->id_number, USTATE); |
494 |
|
|
inodirty(); |
495 |
|
|
} |
496 |
|
|
} |
497 |
|
|
|
498 |
|
|
int |
499 |
|
|
findname(struct inodesc *idesc) |
500 |
|
|
{ |
501 |
|
|
struct direct *dirp = idesc->id_dirp; |
502 |
|
|
|
503 |
|
|
if (dirp->d_ino != idesc->id_parent) |
504 |
|
|
return (KEEPON); |
505 |
|
|
memcpy(idesc->id_name, dirp->d_name, (size_t)dirp->d_namlen + 1); |
506 |
|
|
return (STOP|FOUND); |
507 |
|
|
} |
508 |
|
|
|
509 |
|
|
int |
510 |
|
|
findino(struct inodesc *idesc) |
511 |
|
|
{ |
512 |
|
|
struct direct *dirp = idesc->id_dirp; |
513 |
|
|
|
514 |
|
|
if (dirp->d_ino == 0) |
515 |
|
|
return (KEEPON); |
516 |
|
|
if (strcmp(dirp->d_name, idesc->id_name) == 0 && |
517 |
|
|
dirp->d_ino >= ROOTINO && dirp->d_ino <= maxino) { |
518 |
|
|
idesc->id_parent = dirp->d_ino; |
519 |
|
|
return (STOP|FOUND); |
520 |
|
|
} |
521 |
|
|
return (KEEPON); |
522 |
|
|
} |
523 |
|
|
|
524 |
|
|
void |
525 |
|
|
pinode(ino_t ino) |
526 |
|
|
{ |
527 |
|
|
union dinode *dp; |
528 |
|
|
char *p; |
529 |
|
|
struct passwd *pw; |
530 |
|
|
time_t t; |
531 |
|
|
|
532 |
|
|
printf(" I=%llu ", (unsigned long long)ino); |
533 |
|
|
if (ino < ROOTINO || ino > maxino) |
534 |
|
|
return; |
535 |
|
|
dp = ginode(ino); |
536 |
|
|
printf(" OWNER="); |
537 |
|
|
#ifndef SMALL |
538 |
|
|
if ((pw = getpwuid(DIP(dp, di_uid))) != 0) |
539 |
|
|
printf("%s ", pw->pw_name); |
540 |
|
|
else |
541 |
|
|
#endif |
542 |
|
|
printf("%u ", (unsigned)DIP(dp, di_uid)); |
543 |
|
|
printf("MODE=%o\n", DIP(dp, di_mode)); |
544 |
|
|
if (preen) |
545 |
|
|
printf("%s: ", cdevname()); |
546 |
|
|
printf("SIZE=%llu ", (unsigned long long)DIP(dp, di_size)); |
547 |
|
|
t = DIP(dp, di_mtime); |
548 |
|
|
p = ctime(&t); |
549 |
|
|
printf("MTIME=%12.12s %4.4s ", &p[4], &p[20]); |
550 |
|
|
} |
551 |
|
|
|
552 |
|
|
void |
553 |
|
|
blkerror(ino_t ino, char *type, daddr_t blk) |
554 |
|
|
{ |
555 |
|
|
|
556 |
|
|
pfatal("%lld %s I=%llu", blk, type, (unsigned long long)ino); |
557 |
|
|
printf("\n"); |
558 |
|
|
switch (GET_ISTATE(ino)) { |
559 |
|
|
|
560 |
|
|
case FSTATE: |
561 |
|
|
SET_ISTATE(ino, FCLEAR); |
562 |
|
|
return; |
563 |
|
|
|
564 |
|
|
case DSTATE: |
565 |
|
|
SET_ISTATE(ino, DCLEAR); |
566 |
|
|
return; |
567 |
|
|
|
568 |
|
|
case FCLEAR: |
569 |
|
|
case DCLEAR: |
570 |
|
|
return; |
571 |
|
|
|
572 |
|
|
default: |
573 |
|
|
errexit("BAD STATE %d TO BLKERR\n", GET_ISTATE(ino)); |
574 |
|
|
/* NOTREACHED */ |
575 |
|
|
} |
576 |
|
|
} |
577 |
|
|
|
578 |
|
|
/* |
579 |
|
|
* allocate an unused inode |
580 |
|
|
*/ |
581 |
|
|
ino_t |
582 |
|
|
allocino(ino_t request, int type) |
583 |
|
|
{ |
584 |
|
|
ino_t ino; |
585 |
|
|
union dinode *dp; |
586 |
|
|
struct cg *cgp = &cgrp; |
587 |
|
|
int cg; |
588 |
|
|
time_t t; |
589 |
|
|
struct inostat *info; |
590 |
|
|
|
591 |
|
|
if (request == 0) |
592 |
|
|
request = ROOTINO; |
593 |
|
|
else if (GET_ISTATE(request) != USTATE) |
594 |
|
|
return (0); |
595 |
|
|
for (ino = request; ino < maxino; ino++) |
596 |
|
|
if (GET_ISTATE(ino) == USTATE) |
597 |
|
|
break; |
598 |
|
|
if (ino == maxino) |
599 |
|
|
return (0); |
600 |
|
|
cg = ino_to_cg(&sblock, ino); |
601 |
|
|
/* If necessary, extend the inoinfo array. grow exponentially */ |
602 |
|
|
if ((ino % sblock.fs_ipg) >= (uint64_t)inostathead[cg].il_numalloced) { |
603 |
|
|
unsigned long newalloced, i; |
604 |
|
|
newalloced = MINIMUM(sblock.fs_ipg, |
605 |
|
|
MAXIMUM(2 * inostathead[cg].il_numalloced, 10)); |
606 |
|
|
info = calloc(newalloced, sizeof(struct inostat)); |
607 |
|
|
if (info == NULL) { |
608 |
|
|
pwarn("cannot alloc %zu bytes to extend inoinfo\n", |
609 |
|
|
sizeof(struct inostat) * newalloced); |
610 |
|
|
return 0; |
611 |
|
|
} |
612 |
|
|
memmove(info, inostathead[cg].il_stat, |
613 |
|
|
inostathead[cg].il_numalloced * sizeof(*info)); |
614 |
|
|
for (i = inostathead[cg].il_numalloced; i < newalloced; i++) { |
615 |
|
|
info[i].ino_state = USTATE; |
616 |
|
|
} |
617 |
|
|
if (inostathead[cg].il_numalloced) |
618 |
|
|
free(inostathead[cg].il_stat); |
619 |
|
|
inostathead[cg].il_stat = info; |
620 |
|
|
inostathead[cg].il_numalloced = newalloced; |
621 |
|
|
info = inoinfo(ino); |
622 |
|
|
} |
623 |
|
|
getblk(&cgblk, cgtod(&sblock, cg), sblock.fs_cgsize); |
624 |
|
|
if (!cg_chkmagic(cgp)) |
625 |
|
|
pfatal("CG %d: BAD MAGIC NUMBER\n", cg); |
626 |
|
|
setbit(cg_inosused(cgp), ino % sblock.fs_ipg); |
627 |
|
|
cgp->cg_cs.cs_nifree--; |
628 |
|
|
|
629 |
|
|
switch (type & IFMT) { |
630 |
|
|
case IFDIR: |
631 |
|
|
SET_ISTATE(ino, DSTATE); |
632 |
|
|
cgp->cg_cs.cs_ndir++; |
633 |
|
|
break; |
634 |
|
|
case IFREG: |
635 |
|
|
case IFLNK: |
636 |
|
|
SET_ISTATE(ino, FSTATE); |
637 |
|
|
break; |
638 |
|
|
default: |
639 |
|
|
return (0); |
640 |
|
|
} |
641 |
|
|
cgdirty(); |
642 |
|
|
dp = ginode(ino); |
643 |
|
|
DIP_SET(dp, di_db[0], allocblk(1)); |
644 |
|
|
if (DIP(dp, di_db[0]) == 0) { |
645 |
|
|
SET_ISTATE(ino, USTATE); |
646 |
|
|
return (0); |
647 |
|
|
} |
648 |
|
|
DIP_SET(dp, di_mode, type); |
649 |
|
|
DIP_SET(dp, di_uid, geteuid()); |
650 |
|
|
DIP_SET(dp, di_gid, getegid()); |
651 |
|
|
DIP_SET(dp, di_flags, 0); |
652 |
|
|
(void)time(&t); |
653 |
|
|
DIP_SET(dp, di_atime, t); |
654 |
|
|
DIP_SET(dp, di_atimensec, 0); |
655 |
|
|
DIP_SET(dp, di_mtime, t); |
656 |
|
|
DIP_SET(dp, di_mtimensec, 0); |
657 |
|
|
DIP_SET(dp, di_ctime, t); |
658 |
|
|
DIP_SET(dp, di_ctimensec, 0); |
659 |
|
|
DIP_SET(dp, di_size, sblock.fs_fsize); |
660 |
|
|
DIP_SET(dp, di_blocks, btodb(sblock.fs_fsize)); |
661 |
|
|
n_files++; |
662 |
|
|
inodirty(); |
663 |
|
|
SET_ITYPE(ino, IFTODT(type)); |
664 |
|
|
return (ino); |
665 |
|
|
} |
666 |
|
|
|
667 |
|
|
/* |
668 |
|
|
* deallocate an inode |
669 |
|
|
*/ |
670 |
|
|
void |
671 |
|
|
freeino(ino_t ino) |
672 |
|
|
{ |
673 |
|
|
struct inodesc idesc; |
674 |
|
|
union dinode *dp; |
675 |
|
|
|
676 |
|
|
memset(&idesc, 0, sizeof(struct inodesc)); |
677 |
|
|
idesc.id_type = ADDR; |
678 |
|
|
idesc.id_func = pass4check; |
679 |
|
|
idesc.id_number = ino; |
680 |
|
|
dp = ginode(ino); |
681 |
|
|
(void)ckinode(dp, &idesc); |
682 |
|
|
clearinode(dp); |
683 |
|
|
inodirty(); |
684 |
|
|
SET_ISTATE(ino, USTATE); |
685 |
|
|
n_files--; |
686 |
|
|
} |