1 1.28 dholland /* $NetBSD: dir.c,v 1.28 2013/06/23 07:28:36 dholland Exp $ */ 2 1.1 bouyer 3 1.1 bouyer /* 4 1.1 bouyer * Copyright (c) 1980, 1986, 1993 5 1.1 bouyer * The Regents of the University of California. All rights reserved. 6 1.1 bouyer * 7 1.1 bouyer * Redistribution and use in source and binary forms, with or without 8 1.1 bouyer * modification, are permitted provided that the following conditions 9 1.1 bouyer * are met: 10 1.1 bouyer * 1. Redistributions of source code must retain the above copyright 11 1.1 bouyer * notice, this list of conditions and the following disclaimer. 12 1.1 bouyer * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 bouyer * notice, this list of conditions and the following disclaimer in the 14 1.1 bouyer * documentation and/or other materials provided with the distribution. 15 1.8 agc * 3. Neither the name of the University nor the names of its contributors 16 1.8 agc * may be used to endorse or promote products derived from this software 17 1.8 agc * without specific prior written permission. 18 1.8 agc * 19 1.8 agc * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 1.8 agc * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 1.8 agc * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 1.8 agc * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 1.8 agc * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 1.8 agc * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 1.8 agc * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 1.8 agc * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 1.8 agc * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 1.8 agc * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 1.8 agc * SUCH DAMAGE. 30 1.8 agc */ 31 1.8 agc 32 1.8 agc /* 33 1.8 agc * Copyright (c) 1997 Manuel Bouyer. 34 1.8 agc * 35 1.8 agc * Redistribution and use in source and binary forms, with or without 36 1.8 agc * modification, are permitted provided that the following conditions 37 1.8 agc * are met: 38 1.8 agc * 1. Redistributions of source code must retain the above copyright 39 1.8 agc * notice, this list of conditions and the following disclaimer. 40 1.8 agc * 2. Redistributions in binary form must reproduce the above copyright 41 1.8 agc * notice, this list of conditions and the following disclaimer in the 42 1.8 agc * documentation and/or other materials provided with the distribution. 43 1.1 bouyer * 44 1.11 bouyer * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 45 1.11 bouyer * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 46 1.11 bouyer * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 47 1.11 bouyer * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 48 1.11 bouyer * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 49 1.11 bouyer * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 50 1.11 bouyer * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 51 1.11 bouyer * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 52 1.11 bouyer * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 53 1.11 bouyer * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 54 1.1 bouyer */ 55 1.1 bouyer 56 1.2 lukem #include <sys/cdefs.h> 57 1.1 bouyer #ifndef lint 58 1.1 bouyer #if 0 59 1.1 bouyer static char sccsid[] = "@(#)dir.c 8.5 (Berkeley) 12/8/94"; 60 1.1 bouyer #else 61 1.28 dholland __RCSID("$NetBSD: dir.c,v 1.28 2013/06/23 07:28:36 dholland Exp $"); 62 1.1 bouyer #endif 63 1.1 bouyer #endif /* not lint */ 64 1.1 bouyer 65 1.1 bouyer #include <sys/param.h> 66 1.1 bouyer #include <sys/time.h> 67 1.4 bouyer #include <ufs/ufs/dir.h> 68 1.1 bouyer #include <ufs/ext2fs/ext2fs_dinode.h> 69 1.1 bouyer #include <ufs/ext2fs/ext2fs_dir.h> 70 1.1 bouyer #include <ufs/ext2fs/ext2fs.h> 71 1.1 bouyer 72 1.1 bouyer #include <ufs/ufs/dinode.h> /* for IFMT & friends */ 73 1.1 bouyer 74 1.1 bouyer #include <stdio.h> 75 1.1 bouyer #include <stdlib.h> 76 1.1 bouyer #include <string.h> 77 1.18 christos #include <err.h> 78 1.1 bouyer 79 1.1 bouyer #include "fsck.h" 80 1.1 bouyer #include "fsutil.h" 81 1.1 bouyer #include "extern.h" 82 1.1 bouyer 83 1.14 christos const char *lfname = "lost+found"; 84 1.21 lukem int lfmode = 01700; 85 1.20 christos struct ext2fs_dirtemplate emptydir = { 86 1.20 christos .dot_ino = 0, 87 1.27 dholland .dot_reclen = UFS_DIRBLKSIZ, 88 1.20 christos }; 89 1.1 bouyer struct ext2fs_dirtemplate dirhead = { 90 1.20 christos .dot_ino = 0, 91 1.20 christos .dot_reclen = 12, 92 1.20 christos .dot_namlen = 1, 93 1.20 christos .dot_type = EXT2_FT_DIR, 94 1.20 christos .dot_name = ".", 95 1.20 christos .dotdot_ino = 0, 96 1.27 dholland .dotdot_reclen = UFS_DIRBLKSIZ - 12, 97 1.20 christos .dotdot_namlen = 2, 98 1.20 christos .dotdot_type = EXT2_FT_DIR, 99 1.20 christos .dotdot_name = "..", 100 1.1 bouyer }; 101 1.27 dholland #undef UFS_DIRBLKSIZ 102 1.1 bouyer 103 1.12 xtraeme static int expanddir(struct ext2fs_dinode *, char *); 104 1.12 xtraeme static void freedir(ino_t, ino_t); 105 1.12 xtraeme static struct ext2fs_direct *fsck_readdir(struct inodesc *); 106 1.12 xtraeme static struct bufarea *getdirblk(daddr_t, long); 107 1.12 xtraeme static int lftempname(char *, ino_t); 108 1.12 xtraeme static int mkentry(struct inodesc *); 109 1.12 xtraeme static int chgino(struct inodesc *); 110 1.1 bouyer 111 1.1 bouyer /* 112 1.1 bouyer * Propagate connected state through the tree. 113 1.1 bouyer */ 114 1.1 bouyer void 115 1.12 xtraeme propagate(void) 116 1.1 bouyer { 117 1.2 lukem struct inoinfo **inpp, *inp, *pinp; 118 1.1 bouyer struct inoinfo **inpend; 119 1.1 bouyer 120 1.1 bouyer /* 121 1.1 bouyer * Create a list of children for each directory. 122 1.1 bouyer */ 123 1.1 bouyer inpend = &inpsort[inplast]; 124 1.1 bouyer for (inpp = inpsort; inpp < inpend; inpp++) { 125 1.1 bouyer inp = *inpp; 126 1.1 bouyer if (inp->i_parent == 0 || 127 1.1 bouyer inp->i_number == EXT2_ROOTINO) 128 1.1 bouyer continue; 129 1.1 bouyer pinp = getinoinfo(inp->i_parent); 130 1.1 bouyer inp->i_parentp = pinp; 131 1.1 bouyer inp->i_sibling = pinp->i_child; 132 1.1 bouyer pinp->i_child = inp; 133 1.1 bouyer } 134 1.1 bouyer inp = getinoinfo(EXT2_ROOTINO); 135 1.1 bouyer while (inp) { 136 1.1 bouyer statemap[inp->i_number] = DFOUND; 137 1.1 bouyer if (inp->i_child && 138 1.1 bouyer statemap[inp->i_child->i_number] == DSTATE) 139 1.1 bouyer inp = inp->i_child; 140 1.1 bouyer else if (inp->i_sibling) 141 1.1 bouyer inp = inp->i_sibling; 142 1.1 bouyer else 143 1.1 bouyer inp = inp->i_parentp; 144 1.1 bouyer } 145 1.1 bouyer } 146 1.1 bouyer 147 1.1 bouyer /* 148 1.1 bouyer * Scan each entry in a directory block. 149 1.1 bouyer */ 150 1.1 bouyer int 151 1.12 xtraeme dirscan(struct inodesc *idesc) 152 1.1 bouyer { 153 1.2 lukem struct ext2fs_direct *dp; 154 1.2 lukem struct bufarea *bp; 155 1.1 bouyer int dsize, n; 156 1.1 bouyer long blksiz; 157 1.1 bouyer char *dbuf = NULL; 158 1.1 bouyer 159 1.18 christos if ((dbuf = malloc(sblock.e2fs_bsize)) == NULL) 160 1.18 christos err(8, "Can't allocate directory block"); 161 1.1 bouyer 162 1.1 bouyer if (idesc->id_type != DATA) 163 1.22 lukem errexit("wrong type to dirscan %d", idesc->id_type); 164 1.1 bouyer if (idesc->id_entryno == 0 && 165 1.1 bouyer (idesc->id_filesize & (sblock.e2fs_bsize - 1)) != 0) 166 1.1 bouyer idesc->id_filesize = roundup(idesc->id_filesize, sblock.e2fs_bsize); 167 1.1 bouyer blksiz = idesc->id_numfrags * sblock.e2fs_bsize; 168 1.1 bouyer if (chkrange(idesc->id_blkno, idesc->id_numfrags)) { 169 1.1 bouyer idesc->id_filesize -= blksiz; 170 1.18 christos free(dbuf); 171 1.1 bouyer return (SKIP); 172 1.1 bouyer } 173 1.1 bouyer idesc->id_loc = 0; 174 1.1 bouyer for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) { 175 1.3 bouyer dsize = fs2h16(dp->e2d_reclen); 176 1.1 bouyer memcpy(dbuf, dp, (size_t)dsize); 177 1.1 bouyer idesc->id_dirp = (struct ext2fs_direct *)dbuf; 178 1.1 bouyer if ((n = (*idesc->id_func)(idesc)) & ALTERED) { 179 1.1 bouyer bp = getdirblk(idesc->id_blkno, blksiz); 180 1.1 bouyer memcpy(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf, 181 1.1 bouyer (size_t)dsize); 182 1.1 bouyer dirty(bp); 183 1.1 bouyer sbdirty(); 184 1.1 bouyer } 185 1.1 bouyer if (n & STOP) { 186 1.1 bouyer free(dbuf); 187 1.1 bouyer return (n); 188 1.1 bouyer } 189 1.1 bouyer } 190 1.1 bouyer free(dbuf); 191 1.1 bouyer return (idesc->id_filesize > 0 ? KEEPON : STOP); 192 1.1 bouyer } 193 1.1 bouyer 194 1.1 bouyer /* 195 1.1 bouyer * get next entry in a directory. 196 1.1 bouyer */ 197 1.1 bouyer static struct ext2fs_direct * 198 1.12 xtraeme fsck_readdir(struct inodesc *idesc) 199 1.1 bouyer { 200 1.2 lukem struct ext2fs_direct *dp, *ndp; 201 1.2 lukem struct bufarea *bp; 202 1.1 bouyer long size, blksiz, fix, dploc; 203 1.1 bouyer 204 1.1 bouyer blksiz = idesc->id_numfrags * sblock.e2fs_bsize; 205 1.1 bouyer bp = getdirblk(idesc->id_blkno, blksiz); 206 1.1 bouyer if (idesc->id_loc % sblock.e2fs_bsize == 0 && idesc->id_filesize > 0 && 207 1.1 bouyer idesc->id_loc < blksiz) { 208 1.1 bouyer dp = (struct ext2fs_direct *)(bp->b_un.b_buf + idesc->id_loc); 209 1.1 bouyer if (dircheck(idesc, dp)) 210 1.1 bouyer goto dpok; 211 1.1 bouyer if (idesc->id_fix == IGNORE) 212 1.1 bouyer return (0); 213 1.1 bouyer fix = dofix(idesc, "DIRECTORY CORRUPTED"); 214 1.1 bouyer bp = getdirblk(idesc->id_blkno, blksiz); 215 1.1 bouyer dp = (struct ext2fs_direct *)(bp->b_un.b_buf + idesc->id_loc); 216 1.3 bouyer dp->e2d_reclen = h2fs16(sblock.e2fs_bsize); 217 1.1 bouyer dp->e2d_ino = 0; 218 1.1 bouyer dp->e2d_namlen = 0; 219 1.4 bouyer dp->e2d_type = 0; 220 1.1 bouyer dp->e2d_name[0] = '\0'; 221 1.1 bouyer if (fix) 222 1.1 bouyer dirty(bp); 223 1.1 bouyer idesc->id_loc += sblock.e2fs_bsize; 224 1.1 bouyer idesc->id_filesize -= sblock.e2fs_bsize; 225 1.1 bouyer return (dp); 226 1.1 bouyer } 227 1.1 bouyer dpok: 228 1.1 bouyer if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz) 229 1.1 bouyer return NULL; 230 1.1 bouyer dploc = idesc->id_loc; 231 1.1 bouyer dp = (struct ext2fs_direct *)(bp->b_un.b_buf + dploc); 232 1.3 bouyer idesc->id_loc += fs2h16(dp->e2d_reclen); 233 1.3 bouyer idesc->id_filesize -= fs2h16(dp->e2d_reclen); 234 1.1 bouyer if ((idesc->id_loc % sblock.e2fs_bsize) == 0) 235 1.1 bouyer return (dp); 236 1.1 bouyer ndp = (struct ext2fs_direct *)(bp->b_un.b_buf + idesc->id_loc); 237 1.1 bouyer if (idesc->id_loc < blksiz && idesc->id_filesize > 0 && 238 1.1 bouyer dircheck(idesc, ndp) == 0) { 239 1.1 bouyer size = sblock.e2fs_bsize - (idesc->id_loc % sblock.e2fs_bsize); 240 1.1 bouyer idesc->id_loc += size; 241 1.1 bouyer idesc->id_filesize -= size; 242 1.1 bouyer if (idesc->id_fix == IGNORE) 243 1.1 bouyer return (0); 244 1.1 bouyer fix = dofix(idesc, "DIRECTORY CORRUPTED"); 245 1.1 bouyer bp = getdirblk(idesc->id_blkno, blksiz); 246 1.1 bouyer dp = (struct ext2fs_direct *)(bp->b_un.b_buf + dploc); 247 1.3 bouyer dp->e2d_reclen = h2fs16(fs2h16(dp->e2d_reclen) + size); 248 1.1 bouyer if (fix) 249 1.1 bouyer dirty(bp); 250 1.1 bouyer } 251 1.1 bouyer return (dp); 252 1.1 bouyer } 253 1.1 bouyer 254 1.1 bouyer /* 255 1.1 bouyer * Verify that a directory entry is valid. 256 1.1 bouyer * This is a superset of the checks made in the kernel. 257 1.1 bouyer */ 258 1.1 bouyer int 259 1.12 xtraeme dircheck(struct inodesc *idesc, struct ext2fs_direct *dp) 260 1.1 bouyer { 261 1.1 bouyer int size; 262 1.1 bouyer char *cp; 263 1.1 bouyer int spaceleft; 264 1.3 bouyer u_int16_t reclen = fs2h16(dp->e2d_reclen); 265 1.1 bouyer 266 1.1 bouyer spaceleft = sblock.e2fs_bsize - (idesc->id_loc % sblock.e2fs_bsize); 267 1.3 bouyer if (fs2h32(dp->e2d_ino) > maxino || 268 1.3 bouyer reclen == 0 || 269 1.3 bouyer reclen > spaceleft || 270 1.3 bouyer (reclen & 0x3) != 0) 271 1.1 bouyer return (0); 272 1.1 bouyer if (dp->e2d_ino == 0) 273 1.1 bouyer return (1); 274 1.5 bouyer if (sblock.e2fs.e2fs_rev < E2FS_REV1 || 275 1.4 bouyer (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE) == 0) 276 1.4 bouyer if (dp->e2d_type != 0) 277 1.4 bouyer return (1); 278 1.4 bouyer size = EXT2FS_DIRSIZ(dp->e2d_namlen); 279 1.3 bouyer if (reclen < size || 280 1.6 simonb idesc->id_filesize < size /* || 281 1.6 simonb dp->e2d_namlen > EXT2FS_MAXNAMLEN */) 282 1.1 bouyer return (0); 283 1.4 bouyer for (cp = dp->e2d_name, size = 0; size < dp->e2d_namlen; size++) 284 1.1 bouyer if (*cp == '\0' || (*cp++ == '/')) 285 1.1 bouyer return (0); 286 1.1 bouyer return (1); 287 1.1 bouyer } 288 1.1 bouyer 289 1.1 bouyer void 290 1.15 christos direrror(ino_t ino, const char *errmesg) 291 1.1 bouyer { 292 1.1 bouyer 293 1.1 bouyer fileerror(ino, ino, errmesg); 294 1.1 bouyer } 295 1.1 bouyer 296 1.1 bouyer void 297 1.15 christos fileerror(ino_t cwd, ino_t ino, const char *errmesg) 298 1.1 bouyer { 299 1.2 lukem struct ext2fs_dinode *dp; 300 1.1 bouyer char pathbuf[MAXPATHLEN + 1]; 301 1.1 bouyer 302 1.1 bouyer pwarn("%s ", errmesg); 303 1.1 bouyer pinode(ino); 304 1.1 bouyer printf("\n"); 305 1.7 itojun getpathname(pathbuf, sizeof(pathbuf), cwd, ino); 306 1.1 bouyer if ((ino < EXT2_FIRSTINO && ino != EXT2_ROOTINO) || ino > maxino) { 307 1.1 bouyer pfatal("NAME=%s\n", pathbuf); 308 1.1 bouyer return; 309 1.1 bouyer } 310 1.1 bouyer dp = ginode(ino); 311 1.1 bouyer if (ftypeok(dp)) 312 1.1 bouyer pfatal("%s=%s\n", 313 1.3 bouyer (fs2h16(dp->e2di_mode) & IFMT) == IFDIR ? "DIR" : "FILE", pathbuf); 314 1.1 bouyer else 315 1.1 bouyer pfatal("NAME=%s\n", pathbuf); 316 1.1 bouyer } 317 1.1 bouyer 318 1.1 bouyer void 319 1.12 xtraeme adjust(struct inodesc *idesc, short lcnt) 320 1.1 bouyer { 321 1.2 lukem struct ext2fs_dinode *dp; 322 1.1 bouyer 323 1.1 bouyer dp = ginode(idesc->id_number); 324 1.3 bouyer if (fs2h16(dp->e2di_nlink) == lcnt) { 325 1.1 bouyer if (linkup(idesc->id_number, (ino_t)0) == 0) 326 1.1 bouyer clri(idesc, "UNREF", 0); 327 1.1 bouyer } else { 328 1.1 bouyer pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname : 329 1.3 bouyer ((fs2h16(dp->e2di_mode) & IFMT) == IFDIR ? "DIR" : "FILE")); 330 1.1 bouyer pinode(idesc->id_number); 331 1.1 bouyer printf(" COUNT %d SHOULD BE %d", 332 1.3 bouyer fs2h16(dp->e2di_nlink), fs2h16(dp->e2di_nlink) - lcnt); 333 1.1 bouyer if (preen) { 334 1.1 bouyer if (lcnt < 0) { 335 1.1 bouyer printf("\n"); 336 1.1 bouyer pfatal("LINK COUNT INCREASING"); 337 1.1 bouyer } 338 1.1 bouyer printf(" (ADJUSTED)\n"); 339 1.1 bouyer } 340 1.1 bouyer if (preen || reply("ADJUST") == 1) { 341 1.3 bouyer dp->e2di_nlink = h2fs16(fs2h16(dp->e2di_nlink) - lcnt); 342 1.1 bouyer inodirty(); 343 1.1 bouyer } 344 1.1 bouyer } 345 1.1 bouyer } 346 1.1 bouyer 347 1.1 bouyer static int 348 1.12 xtraeme mkentry(struct inodesc *idesc) 349 1.1 bouyer { 350 1.2 lukem struct ext2fs_direct *dirp = idesc->id_dirp; 351 1.1 bouyer struct ext2fs_direct newent; 352 1.1 bouyer int newlen, oldlen; 353 1.1 bouyer 354 1.19 mrg newent.e2d_type = 0; /* XXX gcc */ 355 1.4 bouyer newent.e2d_namlen = strlen(idesc->id_name); 356 1.4 bouyer if (sblock.e2fs.e2fs_rev > E2FS_REV0 && 357 1.4 bouyer (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) 358 1.5 bouyer newent.e2d_type = inot2ext2dt(typemap[idesc->id_parent]); 359 1.4 bouyer newlen = EXT2FS_DIRSIZ(newent.e2d_namlen); 360 1.1 bouyer if (dirp->e2d_ino != 0) 361 1.4 bouyer oldlen = EXT2FS_DIRSIZ(dirp->e2d_namlen); 362 1.1 bouyer else 363 1.1 bouyer oldlen = 0; 364 1.3 bouyer if (fs2h16(dirp->e2d_reclen) - oldlen < newlen) 365 1.1 bouyer return (KEEPON); 366 1.3 bouyer newent.e2d_reclen = h2fs16(fs2h16(dirp->e2d_reclen) - oldlen); 367 1.3 bouyer dirp->e2d_reclen = h2fs16(oldlen); 368 1.1 bouyer dirp = (struct ext2fs_direct *)(((char *)dirp) + oldlen); 369 1.3 bouyer dirp->e2d_ino = h2fs32(idesc->id_parent); /* ino to be entered is in id_parent */ 370 1.1 bouyer dirp->e2d_reclen = newent.e2d_reclen; 371 1.1 bouyer dirp->e2d_namlen = newent.e2d_namlen; 372 1.4 bouyer dirp->e2d_type = newent.e2d_type; 373 1.4 bouyer memcpy(dirp->e2d_name, idesc->id_name, (size_t)(dirp->e2d_namlen)); 374 1.1 bouyer return (ALTERED|STOP); 375 1.1 bouyer } 376 1.1 bouyer 377 1.1 bouyer static int 378 1.12 xtraeme chgino(struct inodesc *idesc) 379 1.1 bouyer { 380 1.2 lukem struct ext2fs_direct *dirp = idesc->id_dirp; 381 1.4 bouyer u_int16_t namlen = dirp->e2d_namlen; 382 1.1 bouyer 383 1.3 bouyer if (strlen(idesc->id_name) != namlen || 384 1.3 bouyer strncmp(dirp->e2d_name, idesc->id_name, (int)namlen)) 385 1.1 bouyer return (KEEPON); 386 1.3 bouyer dirp->e2d_ino = h2fs32(idesc->id_parent); 387 1.4 bouyer if (sblock.e2fs.e2fs_rev > E2FS_REV0 && 388 1.4 bouyer (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) 389 1.5 bouyer dirp->e2d_type = inot2ext2dt(typemap[idesc->id_parent]); 390 1.4 bouyer else 391 1.4 bouyer dirp->e2d_type = 0; 392 1.1 bouyer return (ALTERED|STOP); 393 1.1 bouyer } 394 1.1 bouyer 395 1.1 bouyer int 396 1.12 xtraeme linkup(ino_t orphan, ino_t parentdir) 397 1.1 bouyer { 398 1.2 lukem struct ext2fs_dinode *dp; 399 1.1 bouyer int lostdir; 400 1.1 bouyer ino_t oldlfdir; 401 1.1 bouyer struct inodesc idesc; 402 1.1 bouyer char tempname[BUFSIZ]; 403 1.1 bouyer 404 1.1 bouyer memset(&idesc, 0, sizeof(struct inodesc)); 405 1.1 bouyer dp = ginode(orphan); 406 1.3 bouyer lostdir = (fs2h16(dp->e2di_mode) & IFMT) == IFDIR; 407 1.1 bouyer pwarn("UNREF %s ", lostdir ? "DIR" : "FILE"); 408 1.1 bouyer pinode(orphan); 409 1.13 ws if (preen && inosize(dp) == 0) 410 1.1 bouyer return (0); 411 1.1 bouyer if (preen) 412 1.1 bouyer printf(" (RECONNECTED)\n"); 413 1.1 bouyer else 414 1.1 bouyer if (reply("RECONNECT") == 0) 415 1.1 bouyer return (0); 416 1.1 bouyer if (lfdir == 0) { 417 1.1 bouyer dp = ginode(EXT2_ROOTINO); 418 1.1 bouyer idesc.id_name = lfname; 419 1.1 bouyer idesc.id_type = DATA; 420 1.1 bouyer idesc.id_func = findino; 421 1.1 bouyer idesc.id_number = EXT2_ROOTINO; 422 1.1 bouyer if ((ckinode(dp, &idesc) & FOUND) != 0) { 423 1.1 bouyer lfdir = idesc.id_parent; 424 1.1 bouyer } else { 425 1.1 bouyer pwarn("NO lost+found DIRECTORY"); 426 1.1 bouyer if (preen || reply("CREATE")) { 427 1.1 bouyer lfdir = allocdir(EXT2_ROOTINO, (ino_t)0, lfmode); 428 1.1 bouyer if (lfdir != 0) { 429 1.1 bouyer if (makeentry(EXT2_ROOTINO, lfdir, lfname) != 0) { 430 1.1 bouyer if (preen) 431 1.1 bouyer printf(" (CREATED)\n"); 432 1.1 bouyer } else { 433 1.1 bouyer freedir(lfdir, EXT2_ROOTINO); 434 1.1 bouyer lfdir = 0; 435 1.1 bouyer if (preen) 436 1.1 bouyer printf("\n"); 437 1.1 bouyer } 438 1.1 bouyer } 439 1.1 bouyer } 440 1.1 bouyer } 441 1.1 bouyer if (lfdir == 0) { 442 1.1 bouyer pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY"); 443 1.1 bouyer printf("\n\n"); 444 1.1 bouyer return (0); 445 1.1 bouyer } 446 1.1 bouyer } 447 1.1 bouyer dp = ginode(lfdir); 448 1.3 bouyer if ((fs2h16(dp->e2di_mode) & IFMT) != IFDIR) { 449 1.1 bouyer pfatal("lost+found IS NOT A DIRECTORY"); 450 1.1 bouyer if (reply("REALLOCATE") == 0) 451 1.1 bouyer return (0); 452 1.1 bouyer oldlfdir = lfdir; 453 1.1 bouyer if ((lfdir = allocdir(EXT2_ROOTINO, (ino_t)0, lfmode)) == 0) { 454 1.1 bouyer pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 455 1.1 bouyer return (0); 456 1.1 bouyer } 457 1.1 bouyer if ((changeino(EXT2_ROOTINO, lfname, lfdir) & ALTERED) == 0) { 458 1.1 bouyer pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n"); 459 1.1 bouyer return (0); 460 1.1 bouyer } 461 1.1 bouyer inodirty(); 462 1.1 bouyer idesc.id_type = ADDR; 463 1.1 bouyer idesc.id_func = pass4check; 464 1.1 bouyer idesc.id_number = oldlfdir; 465 1.1 bouyer adjust(&idesc, lncntp[oldlfdir] + 1); 466 1.1 bouyer lncntp[oldlfdir] = 0; 467 1.1 bouyer dp = ginode(lfdir); 468 1.1 bouyer } 469 1.1 bouyer if (statemap[lfdir] != DFOUND) { 470 1.1 bouyer pfatal("SORRY. NO lost+found DIRECTORY\n\n"); 471 1.1 bouyer return (0); 472 1.1 bouyer } 473 1.1 bouyer (void)lftempname(tempname, orphan); 474 1.1 bouyer if (makeentry(lfdir, orphan, tempname) == 0) { 475 1.1 bouyer pfatal("SORRY. NO SPACE IN lost+found DIRECTORY"); 476 1.1 bouyer printf("\n\n"); 477 1.1 bouyer return (0); 478 1.1 bouyer } 479 1.1 bouyer lncntp[orphan]--; 480 1.1 bouyer if (lostdir) { 481 1.1 bouyer if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 && 482 1.1 bouyer parentdir != (ino_t)-1) 483 1.1 bouyer (void)makeentry(orphan, lfdir, ".."); 484 1.1 bouyer dp = ginode(lfdir); 485 1.3 bouyer dp->e2di_nlink = h2fs16(fs2h16(dp->e2di_nlink) +1); 486 1.1 bouyer inodirty(); 487 1.1 bouyer lncntp[lfdir]++; 488 1.16 christos pwarn("DIR I=%llu CONNECTED. ", (unsigned long long)orphan); 489 1.1 bouyer if (parentdir != (ino_t)-1) 490 1.16 christos printf("PARENT WAS I=%llu\n", 491 1.16 christos (unsigned long long)parentdir); 492 1.1 bouyer if (preen == 0) 493 1.1 bouyer printf("\n"); 494 1.1 bouyer } 495 1.1 bouyer return (1); 496 1.1 bouyer } 497 1.1 bouyer 498 1.1 bouyer /* 499 1.1 bouyer * fix an entry in a directory. 500 1.1 bouyer */ 501 1.1 bouyer int 502 1.14 christos changeino(ino_t dir, const char *name, ino_t newnum) 503 1.1 bouyer { 504 1.1 bouyer struct inodesc idesc; 505 1.1 bouyer 506 1.1 bouyer memset(&idesc, 0, sizeof(struct inodesc)); 507 1.1 bouyer idesc.id_type = DATA; 508 1.1 bouyer idesc.id_func = chgino; 509 1.1 bouyer idesc.id_number = dir; 510 1.1 bouyer idesc.id_fix = DONTKNOW; 511 1.1 bouyer idesc.id_name = name; 512 1.1 bouyer idesc.id_parent = newnum; /* new value for name */ 513 1.1 bouyer return (ckinode(ginode(dir), &idesc)); 514 1.1 bouyer } 515 1.1 bouyer 516 1.1 bouyer /* 517 1.1 bouyer * make an entry in a directory 518 1.1 bouyer */ 519 1.1 bouyer int 520 1.14 christos makeentry(ino_t parent, ino_t ino, const char *name) 521 1.1 bouyer { 522 1.1 bouyer struct ext2fs_dinode *dp; 523 1.1 bouyer struct inodesc idesc; 524 1.1 bouyer char pathbuf[MAXPATHLEN + 1]; 525 1.1 bouyer 526 1.1 bouyer if ((parent < EXT2_FIRSTINO && parent != EXT2_ROOTINO) 527 1.1 bouyer || parent >= maxino || 528 1.1 bouyer (ino < EXT2_FIRSTINO && ino < EXT2_ROOTINO) || ino >= maxino) 529 1.1 bouyer return (0); 530 1.1 bouyer memset(&idesc, 0, sizeof(struct inodesc)); 531 1.1 bouyer idesc.id_type = DATA; 532 1.1 bouyer idesc.id_func = mkentry; 533 1.1 bouyer idesc.id_number = parent; 534 1.1 bouyer idesc.id_parent = ino; /* this is the inode to enter */ 535 1.1 bouyer idesc.id_fix = DONTKNOW; 536 1.1 bouyer idesc.id_name = name; 537 1.1 bouyer dp = ginode(parent); 538 1.13 ws if (inosize(dp) % sblock.e2fs_bsize) { 539 1.13 ws inossize(dp, roundup(inosize(dp), sblock.e2fs_bsize)); 540 1.1 bouyer inodirty(); 541 1.1 bouyer } 542 1.1 bouyer if ((ckinode(dp, &idesc) & ALTERED) != 0) 543 1.1 bouyer return (1); 544 1.7 itojun getpathname(pathbuf, sizeof(pathbuf), parent, parent); 545 1.1 bouyer dp = ginode(parent); 546 1.1 bouyer if (expanddir(dp, pathbuf) == 0) 547 1.1 bouyer return (0); 548 1.1 bouyer return (ckinode(dp, &idesc) & ALTERED); 549 1.1 bouyer } 550 1.1 bouyer 551 1.1 bouyer /* 552 1.1 bouyer * Attempt to expand the size of a directory 553 1.1 bouyer */ 554 1.1 bouyer static int 555 1.12 xtraeme expanddir(struct ext2fs_dinode *dp, char *name) 556 1.1 bouyer { 557 1.1 bouyer daddr_t lastbn, newblk; 558 1.2 lukem struct bufarea *bp; 559 1.3 bouyer char *firstblk; 560 1.1 bouyer 561 1.28 dholland lastbn = ext2_lblkno(&sblock, inosize(dp)); 562 1.26 dholland if (lastbn >= EXT2FS_NDADDR - 1 || fs2h32(dp->e2di_blocks[lastbn]) == 0 || 563 1.17 christos inosize(dp) == 0) { 564 1.1 bouyer return (0); 565 1.17 christos } 566 1.17 christos if ((newblk = allocblk()) == 0) { 567 1.1 bouyer return (0); 568 1.17 christos } 569 1.1 bouyer dp->e2di_blocks[lastbn + 1] = dp->e2di_blocks[lastbn]; 570 1.3 bouyer dp->e2di_blocks[lastbn] = h2fs32(newblk); 571 1.13 ws inossize(dp, inosize(dp) + sblock.e2fs_bsize); 572 1.25 jakllsch inosnblock(dp, inonblock(dp) + btodb(sblock.e2fs_bsize)); 573 1.3 bouyer bp = getdirblk(fs2h32(dp->e2di_blocks[lastbn + 1]), 574 1.1 bouyer sblock.e2fs_bsize); 575 1.1 bouyer if (bp->b_errs) 576 1.1 bouyer goto bad; 577 1.17 christos if ((firstblk = malloc(sblock.e2fs_bsize)) == NULL) 578 1.17 christos err(8, "cannot allocate first block"); 579 1.1 bouyer memcpy(firstblk, bp->b_un.b_buf, sblock.e2fs_bsize); 580 1.1 bouyer bp = getdirblk(newblk, sblock.e2fs_bsize); 581 1.17 christos if (bp->b_errs) { 582 1.17 christos free(firstblk); 583 1.1 bouyer goto bad; 584 1.17 christos } 585 1.1 bouyer memcpy(bp->b_un.b_buf, firstblk, sblock.e2fs_bsize); 586 1.17 christos free(firstblk); 587 1.1 bouyer dirty(bp); 588 1.3 bouyer bp = getdirblk(fs2h32(dp->e2di_blocks[lastbn + 1]), 589 1.1 bouyer sblock.e2fs_bsize); 590 1.1 bouyer if (bp->b_errs) 591 1.1 bouyer goto bad; 592 1.3 bouyer emptydir.dot_reclen = h2fs16(sblock.e2fs_bsize); 593 1.1 bouyer memcpy(bp->b_un.b_buf, &emptydir, sizeof emptydir); 594 1.1 bouyer pwarn("NO SPACE LEFT IN %s", name); 595 1.1 bouyer if (preen) 596 1.1 bouyer printf(" (EXPANDED)\n"); 597 1.1 bouyer else if (reply("EXPAND") == 0) 598 1.1 bouyer goto bad; 599 1.1 bouyer dirty(bp); 600 1.1 bouyer inodirty(); 601 1.1 bouyer return (1); 602 1.1 bouyer bad: 603 1.1 bouyer dp->e2di_blocks[lastbn] = dp->e2di_blocks[lastbn + 1]; 604 1.1 bouyer dp->e2di_blocks[lastbn + 1] = 0; 605 1.13 ws inossize(dp, inosize(dp) - sblock.e2fs_bsize); 606 1.25 jakllsch inosnblock(dp, inonblock(dp) - btodb(sblock.e2fs_bsize)); 607 1.1 bouyer freeblk(newblk); 608 1.1 bouyer return (0); 609 1.1 bouyer } 610 1.1 bouyer 611 1.1 bouyer /* 612 1.1 bouyer * allocate a new directory 613 1.1 bouyer */ 614 1.1 bouyer int 615 1.12 xtraeme allocdir(ino_t parent, ino_t request, int mode) 616 1.1 bouyer { 617 1.1 bouyer ino_t ino; 618 1.1 bouyer struct ext2fs_dinode *dp; 619 1.2 lukem struct bufarea *bp; 620 1.1 bouyer struct ext2fs_dirtemplate *dirp; 621 1.1 bouyer 622 1.1 bouyer ino = allocino(request, IFDIR|mode); 623 1.3 bouyer dirhead.dot_reclen = h2fs16(12); /* XXX */ 624 1.3 bouyer dirhead.dotdot_reclen = h2fs16(sblock.e2fs_bsize - 12); /* XXX */ 625 1.4 bouyer dirhead.dot_namlen = 1; 626 1.4 bouyer if (sblock.e2fs.e2fs_rev > E2FS_REV0 && 627 1.4 bouyer (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) 628 1.5 bouyer dirhead.dot_type = EXT2_FT_DIR; 629 1.4 bouyer else 630 1.4 bouyer dirhead.dot_type = 0; 631 1.4 bouyer dirhead.dotdot_namlen = 2; 632 1.4 bouyer if (sblock.e2fs.e2fs_rev > E2FS_REV0 && 633 1.4 bouyer (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) 634 1.5 bouyer dirhead.dotdot_type = EXT2_FT_DIR; 635 1.4 bouyer else 636 1.4 bouyer dirhead.dotdot_type = 0; 637 1.1 bouyer dirp = &dirhead; 638 1.3 bouyer dirp->dot_ino = h2fs32(ino); 639 1.3 bouyer dirp->dotdot_ino = h2fs32(parent); 640 1.1 bouyer dp = ginode(ino); 641 1.3 bouyer bp = getdirblk(fs2h32(dp->e2di_blocks[0]), sblock.e2fs_bsize); 642 1.1 bouyer if (bp->b_errs) { 643 1.1 bouyer freeino(ino); 644 1.1 bouyer return (0); 645 1.1 bouyer } 646 1.1 bouyer memcpy(bp->b_un.b_buf, dirp, sizeof(struct ext2fs_dirtemplate)); 647 1.1 bouyer dirty(bp); 648 1.3 bouyer dp->e2di_nlink = h2fs16(2); 649 1.1 bouyer inodirty(); 650 1.1 bouyer if (ino == EXT2_ROOTINO) { 651 1.3 bouyer lncntp[ino] = fs2h16(dp->e2di_nlink); 652 1.1 bouyer cacheino(dp, ino); 653 1.1 bouyer return(ino); 654 1.1 bouyer } 655 1.1 bouyer if (statemap[parent] != DSTATE && statemap[parent] != DFOUND) { 656 1.1 bouyer freeino(ino); 657 1.1 bouyer return (0); 658 1.1 bouyer } 659 1.1 bouyer cacheino(dp, ino); 660 1.1 bouyer statemap[ino] = statemap[parent]; 661 1.1 bouyer if (statemap[ino] == DSTATE) { 662 1.3 bouyer lncntp[ino] = fs2h16(dp->e2di_nlink); 663 1.1 bouyer lncntp[parent]++; 664 1.1 bouyer } 665 1.1 bouyer dp = ginode(parent); 666 1.3 bouyer dp->e2di_nlink = h2fs16(fs2h16(dp->e2di_nlink) + 1); 667 1.1 bouyer inodirty(); 668 1.1 bouyer return (ino); 669 1.1 bouyer } 670 1.1 bouyer 671 1.1 bouyer /* 672 1.1 bouyer * free a directory inode 673 1.1 bouyer */ 674 1.1 bouyer static void 675 1.12 xtraeme freedir(ino_t ino, ino_t parent) 676 1.1 bouyer { 677 1.1 bouyer struct ext2fs_dinode *dp; 678 1.1 bouyer 679 1.1 bouyer if (ino != parent) { 680 1.1 bouyer dp = ginode(parent); 681 1.3 bouyer dp->e2di_nlink = h2fs16(fs2h16(dp->e2di_nlink) - 1); 682 1.1 bouyer inodirty(); 683 1.1 bouyer } 684 1.1 bouyer freeino(ino); 685 1.1 bouyer } 686 1.1 bouyer 687 1.1 bouyer /* 688 1.1 bouyer * generate a temporary name for the lost+found directory. 689 1.1 bouyer */ 690 1.1 bouyer static int 691 1.12 xtraeme lftempname(char *bufp, ino_t ino) 692 1.1 bouyer { 693 1.2 lukem ino_t in; 694 1.2 lukem char *cp; 695 1.1 bouyer int namlen; 696 1.1 bouyer 697 1.1 bouyer cp = bufp + 2; 698 1.1 bouyer for (in = maxino; in > 0; in /= 10) 699 1.1 bouyer cp++; 700 1.1 bouyer *--cp = 0; 701 1.1 bouyer namlen = cp - bufp; 702 1.1 bouyer in = ino; 703 1.1 bouyer while (cp > bufp) { 704 1.1 bouyer *--cp = (in % 10) + '0'; 705 1.1 bouyer in /= 10; 706 1.1 bouyer } 707 1.1 bouyer *cp = '#'; 708 1.1 bouyer return (namlen); 709 1.1 bouyer } 710 1.1 bouyer 711 1.1 bouyer /* 712 1.1 bouyer * Get a directory block. 713 1.1 bouyer * Insure that it is held until another is requested. 714 1.1 bouyer */ 715 1.1 bouyer static struct bufarea * 716 1.12 xtraeme getdirblk(daddr_t blkno, long size) 717 1.1 bouyer { 718 1.1 bouyer 719 1.1 bouyer if (pdirbp != 0) 720 1.1 bouyer pdirbp->b_flags &= ~B_INUSE; 721 1.1 bouyer pdirbp = getdatablk(blkno, size); 722 1.1 bouyer return (pdirbp); 723 1.1 bouyer } 724