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