Exemplo n.º 1
0
void
adjust(struct inodesc *idesc, short lcnt)
{
	struct ext2fs_dinode *dp;

	dp = ginode(idesc->id_number);
	if (fs2h16(dp->e2di_nlink) == lcnt) {
		if (linkup(idesc->id_number, (ino_t)0) == 0)
			clri(idesc, "UNREF", 0);
	} else {
		pwarn("LINK COUNT %s", (lfdir == idesc->id_number) ? lfname :
			((fs2h16(dp->e2di_mode) & IFMT) == IFDIR ? "DIR" : "FILE"));
		pinode(idesc->id_number);
		printf(" COUNT %d SHOULD BE %d",
			fs2h16(dp->e2di_nlink), fs2h16(dp->e2di_nlink) - lcnt);
		if (preen) {
			if (lcnt < 0) {
				printf("\n");
				pfatal("LINK COUNT INCREASING");
			}
			printf(" (ADJUSTED)\n");
		}
		if (preen || reply("ADJUST") == 1) {
			dp->e2di_nlink = h2fs16(fs2h16(dp->e2di_nlink) - lcnt);
			inodirty();
		}
	}
}
Exemplo n.º 2
0
static int
mkentry(struct inodesc *idesc)
{
	struct ext2fs_direct *dirp = idesc->id_dirp;
	struct ext2fs_direct newent;
	int newlen, oldlen;

	newent.e2d_type = 0;	/* XXX gcc */
	newent.e2d_namlen = strlen(idesc->id_name);
	if (sblock.e2fs.e2fs_rev > E2FS_REV0 &&
	    (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE))
		newent.e2d_type = inot2ext2dt(typemap[idesc->id_parent]);
	newlen = EXT2FS_DIRSIZ(newent.e2d_namlen);
	if (dirp->e2d_ino != 0)
		oldlen = EXT2FS_DIRSIZ(dirp->e2d_namlen);
	else
		oldlen = 0;
	if (fs2h16(dirp->e2d_reclen) - oldlen < newlen)
		return (KEEPON);
	newent.e2d_reclen = h2fs16(fs2h16(dirp->e2d_reclen) - oldlen);
	dirp->e2d_reclen = h2fs16(oldlen);
	dirp = (struct ext2fs_direct *)(((char *)dirp) + oldlen);
	dirp->e2d_ino = h2fs32(idesc->id_parent); /* ino to be entered is in id_parent */
	dirp->e2d_reclen = newent.e2d_reclen;
	dirp->e2d_namlen = newent.e2d_namlen;
	dirp->e2d_type = newent.e2d_type;
	memcpy(dirp->e2d_name, idesc->id_name, (size_t)(dirp->e2d_namlen));
	return (ALTERED|STOP);
}
Exemplo n.º 3
0
/*
 * get next entry in a directory.
 */
static struct ext2fs_direct *
fsck_readdir(struct inodesc *idesc)
{
	struct ext2fs_direct *dp, *ndp;
	struct bufarea *bp;
	long size, blksiz, fix, dploc;

	blksiz = idesc->id_numfrags * sblock.e2fs_bsize;
	bp = getdirblk(idesc->id_blkno, blksiz);
	if (idesc->id_loc % sblock.e2fs_bsize == 0 && idesc->id_filesize > 0 &&
	    idesc->id_loc < blksiz) {
		dp = (struct ext2fs_direct *)(bp->b_un.b_buf + idesc->id_loc);
		if (dircheck(idesc, dp))
			goto dpok;
		if (idesc->id_fix == IGNORE)
			return (0);
		fix = dofix(idesc, "DIRECTORY CORRUPTED");
		bp = getdirblk(idesc->id_blkno, blksiz);
		dp = (struct ext2fs_direct *)(bp->b_un.b_buf + idesc->id_loc);
		dp->e2d_reclen = h2fs16(sblock.e2fs_bsize);
		dp->e2d_ino = 0;
		dp->e2d_namlen = 0;
		dp->e2d_type = 0;
		dp->e2d_name[0] = '\0';
		if (fix)
			dirty(bp);
		idesc->id_loc += sblock.e2fs_bsize;
		idesc->id_filesize -= sblock.e2fs_bsize;
		return (dp);
	}
dpok:
	if (idesc->id_filesize <= 0 || idesc->id_loc >= blksiz)
		return NULL;
	dploc = idesc->id_loc;
	dp = (struct ext2fs_direct *)(bp->b_un.b_buf + dploc);
	idesc->id_loc += fs2h16(dp->e2d_reclen);
	idesc->id_filesize -= fs2h16(dp->e2d_reclen);
	if ((idesc->id_loc % sblock.e2fs_bsize) == 0)
		return (dp);
	ndp = (struct ext2fs_direct *)(bp->b_un.b_buf + idesc->id_loc);
	if (idesc->id_loc < blksiz && idesc->id_filesize > 0 &&
	    dircheck(idesc, ndp) == 0) {
		size = sblock.e2fs_bsize - (idesc->id_loc % sblock.e2fs_bsize);
		idesc->id_loc += size;
		idesc->id_filesize -= size;
		if (idesc->id_fix == IGNORE)
			return (0);
		fix = dofix(idesc, "DIRECTORY CORRUPTED");
		bp = getdirblk(idesc->id_blkno, blksiz);
		dp = (struct ext2fs_direct *)(bp->b_un.b_buf + dploc);
		dp->e2d_reclen = h2fs16(fs2h16(dp->e2d_reclen) + size);
		if (fix)
			dirty(bp);
	}
	return (dp);
}
Exemplo n.º 4
0
/*
 * allocate a new directory
 */
int
allocdir(ino_t parent, ino_t request, int mode)
{
	ino_t ino;
	struct ext2fs_dinode *dp;
	struct bufarea *bp;
	struct ext2fs_dirtemplate *dirp;

	ino = allocino(request, IFDIR|mode);
	dirhead.dot_reclen = h2fs16(12); /* XXX */
	dirhead.dotdot_reclen = h2fs16(sblock.e2fs_bsize - 12); /* XXX */
	dirhead.dot_namlen = 1;
	if (sblock.e2fs.e2fs_rev > E2FS_REV0 &&
	    (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE))
		dirhead.dot_type = EXT2_FT_DIR;
	else
		dirhead.dot_type = 0;
	dirhead.dotdot_namlen = 2;
	if (sblock.e2fs.e2fs_rev > E2FS_REV0 &&
	    (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE))
		dirhead.dotdot_type = EXT2_FT_DIR;
	else
		dirhead.dotdot_type = 0;
	dirp = &dirhead;
	dirp->dot_ino = h2fs32(ino);
	dirp->dotdot_ino = h2fs32(parent);
	dp = ginode(ino);
	bp = getdirblk(fs2h32(dp->e2di_blocks[0]), sblock.e2fs_bsize);
	if (bp->b_errs) {
		freeino(ino);
		return (0);
	}
	memcpy(bp->b_un.b_buf, dirp, sizeof(struct ext2fs_dirtemplate));
	dirty(bp);
	dp->e2di_nlink = h2fs16(2);
	inodirty();
	if (ino == EXT2_ROOTINO) {
		lncntp[ino] = fs2h16(dp->e2di_nlink);
		cacheino(dp, ino);
		return(ino);
	}
	if (statemap[parent] != DSTATE && statemap[parent] != DFOUND) {
		freeino(ino);
		return (0);
	}
	cacheino(dp, ino);
	statemap[ino] = statemap[parent];
	if (statemap[ino] == DSTATE) {
		lncntp[ino] = fs2h16(dp->e2di_nlink);
		lncntp[parent]++;
	}
	dp = ginode(parent);
	dp->e2di_nlink = h2fs16(fs2h16(dp->e2di_nlink) + 1);
	inodirty();
	return (ino);
}
Exemplo n.º 5
0
__compactcall void
ext2fs_ls(struct open_file *f, const char *pattern)
{
	struct file *fp = (struct file *)f->f_fsdata;
	size_t block_size = fp->f_fs->e2fs_bsize;
	char *buf;
	size_t buf_size;
	lsentry_t *names = NULL;

	fp->f_seekp = 0;
	while (fp->f_seekp < (off_t)fp->f_di.e2di_size) {
		struct ext2fs_direct  *dp, *edp;
		int rc = buf_read_file(f, &buf, &buf_size);
		if (rc)
			goto out;
		if (buf_size != block_size || buf_size == 0)
			goto out;

		dp = (struct ext2fs_direct *)buf;
		edp = (struct ext2fs_direct *)(buf + buf_size);

		for (; dp < edp;
		     dp = (void *)((char *)dp + fs2h16(dp->e2d_reclen))) {
			const char *t;

			if (fs2h16(dp->e2d_reclen) <= 0)
				goto out;

			if (fs2h32(dp->e2d_ino) == 0)
				continue;

			if (dp->e2d_type >= NELEM(typestr) ||
			    !(t = typestr[dp->e2d_type])) {
				/*
				 * This does not handle "old"
				 * filesystems properly. On little
				 * endian machines, we get a bogus
				 * type name if the namlen matches a
				 * valid type identifier. We could
				 * check if we read namlen "0" and
				 * handle this case specially, if
				 * there were a pressing need...
				 */
				printf("bad dir entry\n");
				goto out;
			}
			lsadd(&names, pattern, dp->e2d_name,
			    strlen(dp->e2d_name), fs2h32(dp->e2d_ino), t);
		}
		fp->f_seekp += buf_size;
	}

	lsprint(names);
out:	lsfree(names);
}
/*
 *	changed so that it confirms to ext2fs_check_dir_entry
 */
static int
ext2fs_dirbadentry(struct vnode *dp, struct ext2fs_direct *de,
		int entryoffsetinblock)
{
	struct ufsmount *ump = VFSTOUFS(dp->v_mount);
	int dirblksiz = ump->um_dirblksiz;

		const char *error_msg = NULL;
		int reclen = fs2h16(de->e2d_reclen);
		int namlen = de->e2d_namlen;

		if (reclen < EXT2FS_DIRSIZ(1)) /* e2d_namlen = 1 */
			error_msg = "rec_len is smaller than minimal";
		else if (reclen % 4 != 0)
			error_msg = "rec_len % 4 != 0";
		else if (namlen > EXT2FS_MAXNAMLEN)
			error_msg = "namlen > EXT2FS_MAXNAMLEN";
		else if (reclen < EXT2FS_DIRSIZ(namlen))
			error_msg = "reclen is too small for name_len";
		else if (entryoffsetinblock + reclen > dirblksiz)
			error_msg = "directory entry across blocks";
		else if (fs2h32(de->e2d_ino) >
		    VTOI(dp)->i_e2fs->e2fs.e2fs_icount)
			error_msg = "inode out of bounds";

		if (error_msg != NULL) {
			printf( "bad directory entry: %s\n"
			    "offset=%d, inode=%lu, rec_len=%d, name_len=%d \n",
			    error_msg, entryoffsetinblock,
			    (unsigned long) fs2h32(de->e2d_ino),
			    reclen, namlen);
			panic("ext2fs_dirbadentry");
		}
		return error_msg == NULL ? 0 : 1;
}
Exemplo n.º 7
0
/*
 * Sanity check the disk vnode content, and copy it over to inode structure.
 */
static int
ext2fs_loadvnode_content(struct m_ext2fs *fs, ino_t ino, struct buf *bp, struct inode *ip)
{
	struct ext2fs_dinode *din;
	int error = 0;

	din = (struct ext2fs_dinode *)((char *)bp->b_data + (ino_to_fsbo(fs, ino) * EXT2_DINODE_SIZE(fs)));

	/* sanity checks - inode data NOT byteswapped at this point */
	if (EXT2_DINODE_FITS(din, e2di_extra_isize, EXT2_DINODE_SIZE(fs))
	    && (EXT2_DINODE_SIZE(fs) - EXT2_REV0_DINODE_SIZE) < fs2h16(din->e2di_extra_isize))
	{
		printf("ext2fs: inode %"PRIu64" bad extra_isize %u",
			ino, din->e2di_extra_isize);
		error = EINVAL;
		goto bad;
	}

	/* everything allright, proceed with copy */
	if (ip->i_din.e2fs_din == NULL)
		ip->i_din.e2fs_din = kmem_alloc(EXT2_DINODE_SIZE(fs), KM_SLEEP);

	e2fs_iload(din, ip->i_din.e2fs_din, EXT2_DINODE_SIZE(fs));

	ext2fs_set_inode_guid(ip);

    bad:
	return error;
}
Exemplo n.º 8
0
/*
 * Verify that a directory entry is valid.
 * This is a superset of the checks made in the kernel.
 */
int
dircheck(struct inodesc *idesc, struct ext2fs_direct *dp)
{
	int size;
	char *cp;
	int spaceleft;
	u_int16_t reclen = fs2h16(dp->e2d_reclen);

	spaceleft = sblock.e2fs_bsize - (idesc->id_loc % sblock.e2fs_bsize);
	if (fs2h32(dp->e2d_ino) > maxino ||
	    reclen == 0 ||
	    reclen > spaceleft ||
	    (reclen & 0x3) != 0)
		return (0);
	if (dp->e2d_ino == 0)
		return (1);
	if (sblock.e2fs.e2fs_rev < E2FS_REV1 ||
	    (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE) == 0)
		if (dp->e2d_type != 0)
			return (1);
	size = EXT2FS_DIRSIZ(dp->e2d_namlen);
	if (reclen < size ||
	    idesc->id_filesize < size /* ||
	    dp->e2d_namlen > EXT2FS_MAXNAMLEN */)
		return (0);
	for (cp = dp->e2d_name, size = 0; size < dp->e2d_namlen; size++)
		if (*cp == '\0' || (*cp++ == '/'))
			return (0);
	return (1);
}
Exemplo n.º 9
0
/*
 * Remove a directory entry after a call to namei, using
 * the parameters which it left in nameidata. The entry
 * dp->i_offset contains the offset into the directory of the
 * entry to be eliminated.  The dp->i_count field contains the
 * size of the previous record in the directory.  If this
 * is 0, the first entry is being deleted, so we need only
 * zero the inode number to mark the entry as free.  If the
 * entry is not the first in the directory, we must reclaim
 * the space of the now empty record by adding the record size
 * to the size of the previous entry.
 */
int
ext2fs_dirremove(struct vnode *dvp, struct componentname *cnp)
{
	struct inode *dp;
	struct ext2fs_direct *ep;
	struct buf *bp;
	int error;
	 
	dp = VTOI(dvp);
	if (dp->i_count == 0) {
		/*
		 * First entry in block: set d_ino to zero.
		 */
		error = ext2fs_bufatoff(dp, (off_t)dp->i_offset, (char **)&ep, 
		    &bp);
		if (error != 0)
			return (error);
		ep->e2d_ino = 0;
		error = VOP_BWRITE(bp);
		dp->i_flag |= IN_CHANGE | IN_UPDATE;
		return (error);
	}
	/*
	 * Collapse new free space into previous entry.
	 */
	error = ext2fs_bufatoff(dp, (off_t)(dp->i_offset - dp->i_count),
	    (char **)&ep, &bp);
	if (error != 0)
		return (error);
	ep->e2d_reclen = h2fs16(fs2h16(ep->e2d_reclen) + dp->i_reclen);
	error = VOP_BWRITE(bp);
	dp->i_flag |= IN_CHANGE | IN_UPDATE;
	return (error);
}
Exemplo n.º 10
0
void
inossize(struct ext2fs_dinode *dp, u_int64_t size)
{
	if ((fs2h16(dp->e2di_mode) & IFMT) == IFREG) {
		dp->e2di_size_high = h2fs32(size >> 32);
		if (size > INT32_MAX)
			if (!setlarge())
				return;
	} else if (size > INT32_MAX) {
Exemplo n.º 11
0
u_int64_t
inosize(struct ext2fs_dinode *dp)
{
	u_int64_t size = fs2h32(dp->e2di_size);

	if ((fs2h16(dp->e2di_mode) & IFMT) == IFREG)
		size |= (u_int64_t)fs2h32(dp->e2di_size_high) << 32;
	if (size > INT32_MAX)
		(void)setlarge();
	return size;
}
Exemplo n.º 12
0
int
ftypeok(struct ext2fs_dinode *dp)
{
	switch (fs2h16(dp->e2di_mode) & IFMT) {

	case IFDIR:
	case IFREG:
	case IFBLK:
	case IFCHR:
	case IFLNK:
	case IFSOCK:
	case IFIFO:
		return (1);

	default:
		if (debug)
			printf("bad file type 0%o\n", fs2h16(dp->e2di_mode));
		return (0);
	}
}
Exemplo n.º 13
0
/*
 * free a directory inode
 */
static void
freedir(ino_t ino, ino_t parent)
{
	struct ext2fs_dinode *dp;

	if (ino != parent) {
		dp = ginode(parent);
		dp->e2di_nlink = h2fs16(fs2h16(dp->e2di_nlink) - 1);
		inodirty();
	}
	freeino(ino);
}
Exemplo n.º 14
0
/*
 * Search a directory for a name and return its
 * inode number.
 */
static int
search_directory(const char *name, int length, struct open_file *f,
	ino32_t *inumber_p)
{
	struct file *fp = (struct file *)f->f_fsdata;
	struct ext2fs_direct *dp;
	struct ext2fs_direct *edp;
	char *buf;
	size_t buf_size;
	int namlen;
	int rc;

	fp->f_seekp = 0;
	/* XXX should handle LARGEFILE */
	while (fp->f_seekp < (off_t)fp->f_di.e2di_size) {
		rc = buf_read_file(f, &buf, &buf_size);
		if (rc)
			return rc;

		dp = (struct ext2fs_direct *)buf;
		edp = (struct ext2fs_direct *)(buf + buf_size);
		for (; dp < edp;
		    dp = (void *)((char *)dp + fs2h16(dp->e2d_reclen))) {
			if (fs2h16(dp->e2d_reclen) <= 0)
				break;
			if (fs2h32(dp->e2d_ino) == (ino32_t)0)
				continue;
			namlen = dp->e2d_namlen;
			if (namlen == length &&
			    !memcmp(name, dp->e2d_name, length)) {
				/* found entry */
				*inumber_p = fs2h32(dp->e2d_ino);
				return 0;
			}
		}
		fp->f_seekp += buf_size;
	}
	return ENOENT;
}
Exemplo n.º 15
0
static int
ext2fs_checksb(struct ext2fs *fs, int ronly)
{
	uint32_t u32;

	if (fs2h16(fs->e2fs_magic) != E2FS_MAGIC) {
		return (EINVAL);		/* XXX needs translation */
	}
	if (fs2h32(fs->e2fs_rev) > E2FS_REV1) {
#ifdef DIAGNOSTIC
		printf("ext2fs: unsupported revision number: %x\n",
		    fs2h32(fs->e2fs_rev));
#endif
		return (EINVAL);		/* XXX needs translation */
	}
	if (fs2h32(fs->e2fs_log_bsize) > 2) { /* block size = 1024|2048|4096 */
#ifdef DIAGNOSTIC
		printf("ext2fs: bad block size: %d "
		    "(expected <= 2 for ext2 fs)\n",
		    fs2h32(fs->e2fs_log_bsize));
#endif
		return (EINVAL);	   /* XXX needs translation */
	}
	if (fs2h32(fs->e2fs_rev) > E2FS_REV0) {
		char buf[256];
		if (fs2h32(fs->e2fs_first_ino) != EXT2_FIRSTINO) {
			printf("ext2fs: unsupported first inode position\n");
			return (EINVAL);      /* XXX needs translation */
		}
		u32 = fs2h32(fs->e2fs_features_incompat) & ~EXT2F_INCOMPAT_SUPP;
		if (u32) {
			snprintb(buf, sizeof(buf), EXT2F_INCOMPAT_BITS, u32);
			printf("ext2fs: unsupported incompat features: %s\n",
			    buf);
			return EINVAL;	/* XXX needs translation */
		}
		u32 = fs2h32(fs->e2fs_features_rocompat) & ~EXT2F_ROCOMPAT_SUPP;
		if (!ronly && u32) {
			snprintb(buf, sizeof(buf), EXT2F_ROCOMPAT_BITS, u32);
			printf("ext2fs: unsupported ro-incompat features: %s\n",
			    buf);
			return EROFS;	/* XXX needs translation */
		}
	}
	return (0);
}
/*
 * Check if a directory is empty or not.
 * Inode supplied must be locked.
 *
 * Using a struct dirtemplate here is not precisely
 * what we want, but better than using a struct ext2fs_direct.
 *
 * NB: does not handle corrupted directories.
 */
int
ext2fs_dirempty(struct inode *ip, ino_t parentino, kauth_cred_t cred)
{
	off_t off;
	struct ext2fs_dirtemplate dbuf;
	struct ext2fs_direct *dp = (struct ext2fs_direct *)&dbuf;
	int error, namlen;
	size_t count;

#define	MINDIRSIZ (sizeof (struct ext2fs_dirtemplate) / 2)

	for (off = 0; off < ext2fs_size(ip); off += fs2h16(dp->e2d_reclen)) {
		error = ufs_bufio(UIO_READ, ITOV(ip), (void *)dp, MINDIRSIZ,
		    off, IO_NODELOCKED, cred, &count, NULL);
		/*
		 * Since we read MINDIRSIZ, residual must
		 * be 0 unless we're at end of file.
		 */
		if (error || count != 0)
			return (0);
		/* avoid infinite loops */
		if (dp->e2d_reclen == 0)
			return (0);
		/* skip empty entries */
		if (dp->e2d_ino == 0)
			continue;
		/* accept only "." and ".." */
		namlen = dp->e2d_namlen;
		if (namlen > 2)
			return (0);
		if (dp->e2d_name[0] != '.')
			return (0);
		/*
		 * At this point namlen must be 1 or 2.
		 * 1 implies ".", 2 implies ".." if second
		 * char is also "."
		 */
		if (namlen == 1)
			continue;
		if (dp->e2d_name[1] == '.' && fs2h32(dp->e2d_ino) == parentino)
			continue;
		return (0);
	}
	return (1);
}
Exemplo n.º 17
0
/*
 * Scan each entry in a directory block.
 */
int
dirscan(struct inodesc *idesc)
{
	struct ext2fs_direct *dp;
	struct bufarea *bp;
	int dsize, n;
	long blksiz;
	char *dbuf = NULL;

	if ((dbuf = malloc(sblock.e2fs_bsize)) == NULL)
		err(8, "Can't allocate directory block");

	if (idesc->id_type != DATA)
		errexit("wrong type to dirscan %d", idesc->id_type);
	if (idesc->id_entryno == 0 &&
	    (idesc->id_filesize & (sblock.e2fs_bsize - 1)) != 0)
		idesc->id_filesize = roundup(idesc->id_filesize, sblock.e2fs_bsize);
	blksiz = idesc->id_numfrags * sblock.e2fs_bsize;
	if (chkrange(idesc->id_blkno, idesc->id_numfrags)) {
		idesc->id_filesize -= blksiz;
		free(dbuf);
		return (SKIP);
	}
	idesc->id_loc = 0;
	for (dp = fsck_readdir(idesc); dp != NULL; dp = fsck_readdir(idesc)) {
		dsize = fs2h16(dp->e2d_reclen);
		memcpy(dbuf, dp, (size_t)dsize);
		idesc->id_dirp = (struct ext2fs_direct *)dbuf;
		if ((n = (*idesc->id_func)(idesc)) & ALTERED) {
			bp = getdirblk(idesc->id_blkno, blksiz);
			memcpy(bp->b_un.b_buf + idesc->id_loc - dsize, dbuf,
			    (size_t)dsize);
			dirty(bp);
			sbdirty();
		}
		if (n & STOP) {
			free(dbuf);
			return (n);
		}
	}
	free(dbuf);
	return (idesc->id_filesize > 0 ? KEEPON : STOP);
}
Exemplo n.º 18
0
void
fileerror(ino_t cwd, ino_t ino, const char *errmesg)
{
	struct ext2fs_dinode *dp;
	char pathbuf[MAXPATHLEN + 1];

	pwarn("%s ", errmesg);
	pinode(ino);
	printf("\n");
	getpathname(pathbuf, sizeof(pathbuf), cwd, ino);
	if ((ino < EXT2_FIRSTINO && ino != EXT2_ROOTINO) || ino > maxino) {
		pfatal("NAME=%s\n", pathbuf);
		return;
	}
	dp = ginode(ino);
	if (ftypeok(dp))
		pfatal("%s=%s\n",
		    (fs2h16(dp->e2di_mode) & IFMT) == IFDIR ? "DIR" : "FILE", pathbuf);
	else
		pfatal("NAME=%s\n", pathbuf);
}
Exemplo n.º 19
0
	    (void)ext2fs_setsize(ip, 0);
    	    memset(ip->i_e2fs_blocks, 0, sizeof(ip->i_e2fs_blocks));
        }

	*vpp = vp;
        return (0);
}

static int
ext2fs_checksb(struct ext2fs *fs, int ronly)
{

        if (fs2h16(fs->e2fs_magic) != E2FS_MAGIC) {
                return (EINVAL);                /* XXX needs translation */
        }
        if (fs2h32(fs->e2fs_rev) > E2FS_REV1) {
#ifdef DIAGNOSTIC
                printf("Ext2 fs: unsupported revision number: %x\n",
                    fs2h32(fs->e2fs_rev));
#endif
                return (EINVAL);                /* XXX needs translation */
        }
        if (fs2h32(fs->e2fs_log_bsize) > 2) { /* block size = 1024|2048|4096 */
#ifdef DIAGNOSTIC
                printf("Ext2 fs: bad block size: %d "
                    "(expected <= 2 for ext2 fs)\n",
                    fs2h32(fs->e2fs_log_bsize));
#endif
                return (EINVAL);           /* XXX needs translation */
        }
        if (fs2h32(fs->e2fs_rev) > E2FS_REV0) {
                if (fs2h32(fs->e2fs_first_ino) != EXT2_FIRSTINO) {
                        printf("Ext2 fs: unsupported first inode position\n");
                        return (EINVAL);      /* XXX needs translation */
                }
                if (fs2h32(fs->e2fs_features_incompat) &
                    ~EXT2F_INCOMPAT_SUPP) {
                        printf("Ext2 fs: unsupported optional feature\n");
                        return (EINVAL);      /* XXX needs translation */
/*
 * Remove a directory entry after a call to namei, using
 * the auxiliary results it provided. The entry
 * ulr_offset contains the offset into the directory of the
 * entry to be eliminated.  The ulr_count field contains the
 * size of the previous record in the directory.  If this
 * is 0, the first entry is being deleted, so we need only
 * zero the inode number to mark the entry as free.  If the
 * entry is not the first in the directory, we must reclaim
 * the space of the now empty record by adding the record size
 * to the size of the previous entry.
 */
int
ext2fs_dirremove(struct vnode *dvp, const struct ufs_lookup_results *ulr,
		 struct componentname *cnp)
{
	struct inode *dp;
	struct ext2fs_direct *ep;
	struct buf *bp;
	int error;

	dp = VTOI(dvp);

	if (ulr->ulr_count == 0) {
		/*
		 * First entry in block: set d_ino to zero.
		 */
		error = ext2fs_blkatoff(dvp, (off_t)ulr->ulr_offset,
		    (void *)&ep, &bp);
		if (error != 0)
			return (error);
		ep->e2d_ino = 0;
		error = VOP_BWRITE(bp->b_vp, bp);
		dp->i_flag |= IN_CHANGE | IN_UPDATE;
		return (error);
	}
	/*
	 * Collapse new free space into previous entry.
	 */
	error = ext2fs_blkatoff(dvp, (off_t)(ulr->ulr_offset - ulr->ulr_count),
	    (void *)&ep, &bp);
	if (error != 0)
		return (error);
	ep->e2d_reclen = h2fs16(fs2h16(ep->e2d_reclen) + ulr->ulr_reclen);
	error = VOP_BWRITE(bp->b_vp, bp);
	dp->i_flag |= IN_CHANGE | IN_UPDATE;
	return (error);
}
/*
 * Vnode op for reading directories.
 *
 * Convert the on-disk entries to <sys/dirent.h> entries.
 * the problem is that the conversion will blow up some entries by four bytes,
 * so it can't be done in place. This is too bad. Right now the conversion is
 * done entry by entry, the converted entry is sent via uiomove.
 *
 * XXX allocate a buffer, convert as many entries as possible, then send
 * the whole buffer to uiomove
 */
int
ext2fs_readdir(void *v)
{
	struct vop_readdir_args /* {
		struct vnode *a_vp;
		struct uio *a_uio;
		kauth_cred_t a_cred;
		int **a_eofflag;
		off_t **a_cookies;
		int ncookies;
	} */ *ap = v;
	struct uio *uio = ap->a_uio;
	int error;
	size_t e2fs_count, readcnt;
	struct vnode *vp = ap->a_vp;
	struct m_ext2fs *fs = VTOI(vp)->i_e2fs;

	struct ext2fs_direct *dp;
	struct dirent *dstd;
	struct uio auio;
	struct iovec aiov;
	void *dirbuf;
	off_t off = uio->uio_offset;
	off_t *cookies = NULL;
	int nc = 0, ncookies = 0;
	int e2d_reclen;

	if (vp->v_type != VDIR)
		return (ENOTDIR);

	e2fs_count = uio->uio_resid;
	/* Make sure we don't return partial entries. */
	e2fs_count -= (uio->uio_offset + e2fs_count) & (fs->e2fs_bsize -1);
	if (e2fs_count <= 0)
		return (EINVAL);

	auio = *uio;
	auio.uio_iov = &aiov;
	auio.uio_iovcnt = 1;
	aiov.iov_len = e2fs_count;
	auio.uio_resid = e2fs_count;
	UIO_SETUP_SYSSPACE(&auio);
	dirbuf = kmem_alloc(e2fs_count, KM_SLEEP);
	dstd = kmem_zalloc(sizeof(struct dirent), KM_SLEEP);
	if (ap->a_ncookies) {
		nc = e2fs_count / _DIRENT_MINSIZE((struct dirent *)0);
		ncookies = nc;
		cookies = malloc(sizeof (off_t) * ncookies, M_TEMP, M_WAITOK);
		*ap->a_cookies = cookies;
	}
	aiov.iov_base = dirbuf;

	error = UFS_BUFRD(ap->a_vp, &auio, 0, ap->a_cred);
	if (error == 0) {
		readcnt = e2fs_count - auio.uio_resid;
		for (dp = (struct ext2fs_direct *)dirbuf;
			(char *)dp < (char *)dirbuf + readcnt; ) {
			e2d_reclen = fs2h16(dp->e2d_reclen);
			if (e2d_reclen == 0) {
				error = EIO;
				break;
			}
			ext2fs_dirconv2ffs(dp, dstd);
			if(dstd->d_reclen > uio->uio_resid) {
				break;
			}
			error = uiomove(dstd, dstd->d_reclen, uio);
			if (error != 0) {
				break;
			}
			off = off + e2d_reclen;
			if (cookies != NULL) {
				*cookies++ = off;
				if (--ncookies <= 0){
					break;  /* out of cookies */
				}
			}
			/* advance dp */
			dp = (struct ext2fs_direct *) ((char *)dp + e2d_reclen);
		}
		/* we need to correct uio_offset */
		uio->uio_offset = off;
	}
	kmem_free(dirbuf, e2fs_count);
	kmem_free(dstd, sizeof(*dstd));
	*ap->a_eofflag = ext2fs_size(VTOI(ap->a_vp)) <= uio->uio_offset;
	if (ap->a_ncookies) {
		if (error) {
			free(*ap->a_cookies, M_TEMP);
			*ap->a_ncookies = 0;
			*ap->a_cookies = NULL;
		} else
			*ap->a_ncookies = nc - ncookies;
	}
	return (error);
}
Exemplo n.º 22
0
/*
 * ext2fs_rename_recalculate_fulr: If we have just entered a directory
 * into dvp at tulr, and we were about to remove one at fulr for an
 * entry named fcnp, fulr may be invalid.  So, if necessary,
 * recalculate it.
 */
static int
ext2fs_rename_recalculate_fulr(struct vnode *dvp,
    struct ufs_lookup_results *fulr, const struct ufs_lookup_results *tulr,
    const struct componentname *fcnp)
{
	struct mount *mp;
	struct ufsmount *ump;
	/* XXX int is a silly type for this; blame ufsmount::um_dirblksiz.  */
	int dirblksiz;
	doff_t search_start, search_end;
	doff_t offset;		/* Offset of entry we're examining.  */
	struct buf *bp;		/* I/O block we're examining.  */
	char *dirbuf;		/* Pointer into directory at search_start.  */
	struct ext2fs_direct *ep; /* Pointer to the entry we're examining.  */
	/* XXX direct::d_reclen is 16-bit;
	 * ufs_lookup_results::ulr_reclen is 32-bit.  Blah.  */
	uint32_t reclen;	/* Length of the entry we're examining.  */
	uint32_t prev_reclen;	/* Length of the preceding entry.  */
	int error;

	KASSERT(dvp != NULL);
	KASSERT(dvp->v_mount != NULL);
	KASSERT(VTOI(dvp) != NULL);
	KASSERT(fulr != NULL);
	KASSERT(tulr != NULL);
	KASSERT(fulr != tulr);
	KASSERT(ext2fs_rename_ulr_overlap_p(fulr, tulr));

	mp = dvp->v_mount;
	ump = VFSTOUFS(mp);
	KASSERT(ump != NULL);
	KASSERT(ump == VTOI(dvp)->i_ump);

	dirblksiz = ump->um_dirblksiz;
	KASSERT(0 < dirblksiz);
	KASSERT((dirblksiz & (dirblksiz - 1)) == 0);

	/* A directory block may not span across multiple I/O blocks.  */
	KASSERT(dirblksiz <= mp->mnt_stat.f_iosize);

	/* Find the bounds of the search.  */
	search_start = tulr->ulr_offset;
	KASSERT(fulr->ulr_reclen < (EXT2FS_MAXDIRSIZE - fulr->ulr_offset));
	search_end = (fulr->ulr_offset + fulr->ulr_reclen);

	/* Compaction must happen only within a directory block. (*)  */
	KASSERT(search_start <= search_end);
	KASSERT((search_end - (search_start &~ (dirblksiz - 1))) <= dirblksiz);

	dirbuf = NULL;
	bp = NULL;
	error = ext2fs_blkatoff(dvp, (off_t)search_start, &dirbuf, &bp);
	if (error)
		return error;
	KASSERT(dirbuf != NULL);
	KASSERT(bp != NULL);

	/*
	 * Guarantee we sha'n't go past the end of the buffer we got.
	 * dirbuf is bp->b_data + (search_start & (iosize - 1)), and
	 * the valid range is [bp->b_data, bp->b_data + bp->b_bcount).
	 */
	KASSERT((search_end - search_start) <=
	    (bp->b_bcount - (search_start & (mp->mnt_stat.f_iosize - 1))));

	prev_reclen = fulr->ulr_count;
	offset = search_start;

	/*
	 * Search from search_start to search_end for the entry matching
	 * fcnp, which must be there because we found it before and it
	 * should only at most have moved earlier.
	 */
	for (;;) {
		KASSERT(search_start <= offset);
		KASSERT(offset < search_end);

		/*
		 * Examine the directory entry at offset.
		 */
		ep = (struct ext2fs_direct *)
		    (dirbuf + (offset - search_start));
		reclen = fs2h16(ep->e2d_reclen);

		if (ep->e2d_ino == 0)
			goto next;	/* Entry is unused.  */

		if (fs2h32(ep->e2d_ino) == UFS_WINO)
			goto next;	/* Entry is whiteout.  */

		if (fcnp->cn_namelen != ep->e2d_namlen)
			goto next;	/* Wrong name length.  */

		if (memcmp(ep->e2d_name, fcnp->cn_nameptr, fcnp->cn_namelen))
			goto next;	/* Wrong name.  */

		/* Got it!  */
		break;

next:
		if (! ((reclen < search_end) &&
			(offset < (search_end - reclen)))) {
			brelse(bp, 0);
			return EIO;	/* XXX Panic?  What?  */
		}

		/* We may not move past the search end.  */
		KASSERT(reclen < search_end);
		KASSERT(offset < (search_end - reclen));

		/*
		 * We may not move across a directory block boundary;
		 * see (*) above.
		 */
		KASSERT((offset &~ (dirblksiz - 1)) ==
		    ((offset + reclen) &~ (dirblksiz - 1)));

		prev_reclen = reclen;
		offset += reclen;
	}

	/*
	 * Found the entry.  Record where.
	 */
	fulr->ulr_offset = offset;
	fulr->ulr_reclen = reclen;

	/*
	 * Record the preceding record length, but not if we're at the
	 * start of a directory block.
	 */
	fulr->ulr_count = ((offset & (dirblksiz - 1))? prev_reclen : 0);

	brelse(bp, 0);
	return 0;
}
Exemplo n.º 23
0
int
linkup(ino_t orphan, ino_t parentdir)
{
	struct ext2fs_dinode *dp;
	int lostdir;
	ino_t oldlfdir;
	struct inodesc idesc;
	char tempname[BUFSIZ];

	memset(&idesc, 0, sizeof(struct inodesc));
	dp = ginode(orphan);
	lostdir = (fs2h16(dp->e2di_mode) & IFMT) == IFDIR;
	pwarn("UNREF %s ", lostdir ? "DIR" : "FILE");
	pinode(orphan);
	if (preen && inosize(dp) == 0)
		return (0);
	if (preen)
		printf(" (RECONNECTED)\n");
	else
		if (reply("RECONNECT") == 0)
			return (0);
	if (lfdir == 0) {
		dp = ginode(EXT2_ROOTINO);
		idesc.id_name = lfname;
		idesc.id_type = DATA;
		idesc.id_func = findino;
		idesc.id_number = EXT2_ROOTINO;
		if ((ckinode(dp, &idesc) & FOUND) != 0) {
			lfdir = idesc.id_parent;
		} else {
			pwarn("NO lost+found DIRECTORY");
			if (preen || reply("CREATE")) {
				lfdir = allocdir(EXT2_ROOTINO, (ino_t)0, lfmode);
				if (lfdir != 0) {
					if (makeentry(EXT2_ROOTINO, lfdir, lfname) != 0) {
						if (preen)
							printf(" (CREATED)\n");
					} else {
						freedir(lfdir, EXT2_ROOTINO);
						lfdir = 0;
						if (preen)
							printf("\n");
					}
				}
			}
		}
		if (lfdir == 0) {
			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY");
			printf("\n\n");
			return (0);
		}
	}
	dp = ginode(lfdir);
	if ((fs2h16(dp->e2di_mode) & IFMT) != IFDIR) {
		pfatal("lost+found IS NOT A DIRECTORY");
		if (reply("REALLOCATE") == 0)
			return (0);
		oldlfdir = lfdir;
		if ((lfdir = allocdir(EXT2_ROOTINO, (ino_t)0, lfmode)) == 0) {
			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
			return (0);
		}
		if ((changeino(EXT2_ROOTINO, lfname, lfdir) & ALTERED) == 0) {
			pfatal("SORRY. CANNOT CREATE lost+found DIRECTORY\n\n");
			return (0);
		}
		inodirty();
		idesc.id_type = ADDR;
		idesc.id_func = pass4check;
		idesc.id_number = oldlfdir;
		adjust(&idesc, lncntp[oldlfdir] + 1);
		lncntp[oldlfdir] = 0;
		dp = ginode(lfdir);
	}
	if (statemap[lfdir] != DFOUND) {
		pfatal("SORRY. NO lost+found DIRECTORY\n\n");
		return (0);
	}
	(void)lftempname(tempname, orphan);
	if (makeentry(lfdir, orphan, tempname) == 0) {
		pfatal("SORRY. NO SPACE IN lost+found DIRECTORY");
		printf("\n\n");
		return (0);
	}
	lncntp[orphan]--;
	if (lostdir) {
		if ((changeino(orphan, "..", lfdir) & ALTERED) == 0 &&
		    parentdir != (ino_t)-1)
			(void)makeentry(orphan, lfdir, "..");
		dp = ginode(lfdir);
		dp->e2di_nlink = h2fs16(fs2h16(dp->e2di_nlink) +1);
		inodirty();
		lncntp[lfdir]++;
		pwarn("DIR I=%llu CONNECTED. ", (unsigned long long)orphan);
		if (parentdir != (ino_t)-1)
			printf("PARENT WAS I=%llu\n",
			    (unsigned long long)parentdir);
		if (preen == 0)
			printf("\n");
	}
	return (1);
}
Exemplo n.º 24
0
static int
pass2check(struct inodesc *idesc)
{
	struct ext2fs_direct *dirp = idesc->id_dirp;
	struct inoinfo *inp;
	int n, entrysize, ret = 0;
	struct ext2fs_dinode *dp;
	const char *errmsg;
	struct ext2fs_direct proto;
	char namebuf[MAXPATHLEN + 1];
	char pathbuf[MAXPATHLEN + 1];

	/* 
	 * check for "."
	 */
	if (idesc->id_entryno != 0)
		goto chk1;
	if (fs2h32(dirp->e2d_ino) != 0 && dirp->e2d_namlen == 1 &&
		dirp->e2d_name[0] == '.') {
		if (fs2h32(dirp->e2d_ino) != idesc->id_number) {
			direrror(idesc->id_number, "BAD INODE NUMBER FOR '.'");
			dirp->e2d_ino = h2fs32(idesc->id_number);
			if (reply("FIX") == 1)
				ret |= ALTERED;
		}
		if (sblock.e2fs.e2fs_rev > E2FS_REV0 &&
		    (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)
		    && (dirp->e2d_type != EXT2_FT_DIR)) {
			direrror(idesc->id_number, "BAD TYPE VALUE FOR '.'");
			dirp->e2d_type = EXT2_FT_DIR;
			if (reply("FIX") == 1)
				ret |= ALTERED;
		}
		goto chk1;
	}
	direrror(idesc->id_number, "MISSING '.'");
	proto.e2d_ino = h2fs32(idesc->id_number);
	proto.e2d_namlen = 1;
	if (sblock.e2fs.e2fs_rev > E2FS_REV0 &&
	    (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE))
		proto.e2d_type = EXT2_FT_DIR;
	else
		proto.e2d_type = 0;
	(void)strlcpy(proto.e2d_name, ".", sizeof(proto.e2d_name));
	entrysize = EXT2FS_DIRSIZ(proto.e2d_namlen);
	if (fs2h32(dirp->e2d_ino) != 0 && strcmp(dirp->e2d_name, "..") != 0) {
		pfatal("CANNOT FIX, FIRST ENTRY IN DIRECTORY CONTAINS %s\n",
			dirp->e2d_name);
	} else if (fs2h16(dirp->e2d_reclen) < entrysize) {
		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '.'\n");
	} else if (fs2h16(dirp->e2d_reclen) < 2 * entrysize) {
		proto.e2d_reclen = dirp->e2d_reclen;
		memcpy(dirp, &proto, (size_t)entrysize);
		if (reply("FIX") == 1)
			ret |= ALTERED;
	} else {
		n = fs2h16(dirp->e2d_reclen) - entrysize;
		proto.e2d_reclen = h2fs16(entrysize);
		memcpy(dirp, &proto, (size_t)entrysize);
		idesc->id_entryno++;
		lncntp[fs2h32(dirp->e2d_ino)]--;
		dirp = (struct ext2fs_direct *)((char *)(dirp) + entrysize);
		memset(dirp, 0, (size_t)n);
		dirp->e2d_reclen = h2fs16(n);
		if (reply("FIX") == 1)
			ret |= ALTERED;
	}
chk1:
	if (idesc->id_entryno > 1)
		goto chk2;
	inp = getinoinfo(idesc->id_number);
	proto.e2d_ino = h2fs32(inp->i_parent);
	proto.e2d_namlen = 2;
	if (sblock.e2fs.e2fs_rev > E2FS_REV0 &&
	    (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE))
		proto.e2d_type = EXT2_FT_DIR;
	else
		proto.e2d_type = 0;
	(void)strlcpy(proto.e2d_name, "..", sizeof(proto.e2d_name));
	entrysize = EXT2FS_DIRSIZ(2);
	if (idesc->id_entryno == 0) {
		n = EXT2FS_DIRSIZ(dirp->e2d_namlen);
		if (fs2h16(dirp->e2d_reclen) < n + entrysize)
			goto chk2;
		proto.e2d_reclen = h2fs16(fs2h16(dirp->e2d_reclen) - n);
		dirp->e2d_reclen = h2fs16(n);
		idesc->id_entryno++;
		lncntp[fs2h32(dirp->e2d_ino)]--;
		dirp = (struct ext2fs_direct *)((char *)(dirp) + n);
		memset(dirp, 0, (size_t)fs2h16(proto.e2d_reclen));
		dirp->e2d_reclen = proto.e2d_reclen;
	}
	if (fs2h32(dirp->e2d_ino) != 0 &&
	    dirp->e2d_namlen == 2 && 
	    strncmp(dirp->e2d_name, "..", 2) == 0) {
		inp->i_dotdot = fs2h32(dirp->e2d_ino);
		if (sblock.e2fs.e2fs_rev > E2FS_REV0 &&
		    (sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)
		    && dirp->e2d_type != EXT2_FT_DIR) {
			direrror(idesc->id_number, "BAD TYPE VALUE FOR '..'");
			dirp->e2d_type = EXT2_FT_DIR;
			if (reply("FIX") == 1)
				ret |= ALTERED;
		}
		goto chk2;
	}
	if (fs2h32(dirp->e2d_ino) != 0 &&
		dirp->e2d_namlen == 1 &&
		strncmp(dirp->e2d_name, ".", 1) != 0) {
		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
		pfatal("CANNOT FIX, SECOND ENTRY IN DIRECTORY CONTAINS %s\n",
			dirp->e2d_name);
		inp->i_dotdot = (ino_t)-1;
	} else if (fs2h16(dirp->e2d_reclen) < entrysize) {
		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
		pfatal("CANNOT FIX, INSUFFICIENT SPACE TO ADD '..'\n");
		inp->i_dotdot = (ino_t)-1;
	} else if (inp->i_parent != 0) {
		/*
		 * We know the parent, so fix now.
		 */
		inp->i_dotdot = inp->i_parent;
		fileerror(inp->i_parent, idesc->id_number, "MISSING '..'");
		proto.e2d_reclen = dirp->e2d_reclen;
		memcpy(dirp, &proto, (size_t)entrysize);
		if (reply("FIX") == 1)
			ret |= ALTERED;
	}
	idesc->id_entryno++;
	if (fs2h32(dirp->e2d_ino) != 0)
		lncntp[fs2h32(dirp->e2d_ino)]--;
	return (ret|KEEPON);
chk2:
	if (fs2h32(dirp->e2d_ino) == 0)
		return (ret|KEEPON);
	if (dirp->e2d_namlen <= 2 &&
	    dirp->e2d_name[0] == '.' &&
	    idesc->id_entryno >= 2) {
		if (dirp->e2d_namlen == 1) {
			direrror(idesc->id_number, "EXTRA '.' ENTRY");
			dirp->e2d_ino = 0;
			if (reply("FIX") == 1)
				ret |= ALTERED;
			return (KEEPON | ret);
		}
		if (dirp->e2d_name[1] == '.') {
			direrror(idesc->id_number, "EXTRA '..' ENTRY");
			dirp->e2d_ino = 0;
			if (reply("FIX") == 1)
				ret |= ALTERED;
			return (KEEPON | ret);
		}
	}
	idesc->id_entryno++;
	n = 0;
	if (fs2h32(dirp->e2d_ino) > maxino ||
		(fs2h32(dirp->e2d_ino) < EXT2_FIRSTINO &&
		 fs2h32(dirp->e2d_ino) != EXT2_ROOTINO)) {
		fileerror(idesc->id_number, fs2h32(dirp->e2d_ino), "I OUT OF RANGE");
		n = reply("REMOVE");
	} else {
again:
		switch (statemap[fs2h32(dirp->e2d_ino)]) {
		case USTATE:
			if (idesc->id_entryno <= 2)
				break;
			fileerror(idesc->id_number, fs2h32(dirp->e2d_ino), "UNALLOCATED");
			n = reply("REMOVE");
			break;

		case DCLEAR:
		case FCLEAR:
			if (idesc->id_entryno <= 2)
				break;
			if (statemap[fs2h32(dirp->e2d_ino)] == FCLEAR)
				errmsg = "DUP/BAD";
			else if (!preen)
				errmsg = "ZERO LENGTH DIRECTORY";
			else {
				n = 1;
				break;
			}
			fileerror(idesc->id_number, fs2h32(dirp->e2d_ino), errmsg);
			if ((n = reply("REMOVE")) == 1)
				break;
			dp = ginode(fs2h32(dirp->e2d_ino));
			statemap[fs2h32(dirp->e2d_ino)] =
			    (fs2h16(dp->e2di_mode) & IFMT) == IFDIR ? DSTATE : FSTATE;
			lncntp[fs2h32(dirp->e2d_ino)] = fs2h16(dp->e2di_nlink);
			goto again;

		case DSTATE:
		case DFOUND:
			inp = getinoinfo(fs2h32(dirp->e2d_ino));
			if (inp->i_parent != 0 && idesc->id_entryno > 2) {
				getpathname(pathbuf, sizeof(pathbuf),
				    idesc->id_number, idesc->id_number);
				getpathname(namebuf, sizeof(namebuf),
				    fs2h32(dirp->e2d_ino),
				    fs2h32(dirp->e2d_ino));
				pwarn("%s %s %s\n", pathbuf,
				    "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
				    namebuf);
				if (preen)
					printf(" (IGNORED)\n");
				else if ((n = reply("REMOVE")) == 1)
					break;
			}
			if (idesc->id_entryno > 2)
				inp->i_parent = idesc->id_number;
			/* fall through */

		case FSTATE:
			if (sblock.e2fs.e2fs_rev > E2FS_REV0 &&
			    (sblock.e2fs.e2fs_features_incompat &
				EXT2F_INCOMPAT_FTYPE) &&
			    dirp->e2d_type !=
				inot2ext2dt(typemap[fs2h32(dirp->e2d_ino)])) {
				dirp->e2d_type =
				    inot2ext2dt(typemap[fs2h32(dirp->e2d_ino)]);
				fileerror(idesc->id_number,
				    fs2h32(dirp->e2d_ino),
				    "BAD TYPE VALUE");
				if (reply("FIX") == 1)
					ret |= ALTERED;
			}
			lncntp[fs2h32(dirp->e2d_ino)]--;
			break;

		default:
			errexit("BAD STATE %d FOR INODE I=%d",
			    statemap[fs2h32(dirp->e2d_ino)], fs2h32(dirp->e2d_ino));
		}
	}
	if (n == 0)
		return (ret|KEEPON);
	dirp->e2d_ino = 0;
	return (ret|KEEPON|ALTERED);
}
Exemplo n.º 25
0
void
pass2(void)
{
	struct ext2fs_dinode *dp;
	struct inoinfo **inpp, *inp;
	struct inoinfo **inpend;
	struct inodesc curino;
	struct ext2fs_dinode dino;
	char pathbuf[MAXPATHLEN + 1];

	switch (statemap[EXT2_ROOTINO]) {

	case USTATE:
		pfatal("ROOT INODE UNALLOCATED");
		if (reply("ALLOCATE") == 0)
			exit(FSCK_EXIT_CHECK_FAILED);
		if (allocdir(EXT2_ROOTINO, EXT2_ROOTINO, 0755) != EXT2_ROOTINO)
			errexit("CANNOT ALLOCATE ROOT INODE");
		break;

	case DCLEAR:
		pfatal("DUPS/BAD IN ROOT INODE");
		if (reply("REALLOCATE")) {
			freeino(EXT2_ROOTINO);
			if (allocdir(EXT2_ROOTINO, EXT2_ROOTINO, 0755) != EXT2_ROOTINO)
				errexit("CANNOT ALLOCATE ROOT INODE");
			break;
		}
		if (reply("CONTINUE") == 0)
			exit(FSCK_EXIT_CHECK_FAILED);
		break;

	case FSTATE:
	case FCLEAR:
		pfatal("ROOT INODE NOT DIRECTORY");
		if (reply("REALLOCATE")) {
			freeino(EXT2_ROOTINO);
			if (allocdir(EXT2_ROOTINO, EXT2_ROOTINO, 0755) != EXT2_ROOTINO)
				errexit("CANNOT ALLOCATE ROOT INODE");
			break;
		}
		if (reply("FIX") == 0)
			exit(FSCK_EXIT_CHECK_FAILED);
		dp = ginode(EXT2_ROOTINO);
		dp->e2di_mode = h2fs16((fs2h16(dp->e2di_mode) & ~IFMT) | IFDIR);
		inodirty();
		break;

	case DSTATE:
		break;

	default:
		errexit("BAD STATE %d FOR ROOT INODE", statemap[EXT2_ROOTINO]);
	}

	/*
	 * Sort the directory list into disk block order.
	 */
	qsort((char *)inpsort, (size_t)inplast, sizeof *inpsort, blksort);
	/*
	 * Check the integrity of each directory.
	 */
	memset(&curino, 0, sizeof(struct inodesc));
	curino.id_type = DATA;
	curino.id_func = pass2check;
	inpend = &inpsort[inplast];
	for (inpp = inpsort; inpp < inpend; inpp++) {
		inp = *inpp;
		if (inp->i_isize == 0)
			continue;
		if (inp->i_isize < MINDIRSIZE) {
			direrror(inp->i_number, "DIRECTORY TOO SHORT");
			inp->i_isize = roundup(MINDIRSIZE, sblock.e2fs_bsize);
			if (reply("FIX") == 1) {
				dp = ginode(inp->i_number);
				inossize(dp, inp->i_isize);
				inodirty();
			}
		} else if ((inp->i_isize & (sblock.e2fs_bsize - 1)) != 0) {
			getpathname(pathbuf, sizeof(pathbuf), inp->i_number,
			    inp->i_number);
			pwarn("DIRECTORY %s: LENGTH %lu NOT MULTIPLE OF %d",
			    pathbuf, (u_long)inp->i_isize, sblock.e2fs_bsize);
			if (preen)
				printf(" (ADJUSTED)\n");
			inp->i_isize = roundup(inp->i_isize, sblock.e2fs_bsize);
			if (preen || reply("ADJUST") == 1) {
				dp = ginode(inp->i_number);
				inossize(dp, inp->i_isize);
				inodirty();
			}
		}
		memset(&dino, 0, sizeof(struct ext2fs_dinode));
		dino.e2di_mode = h2fs16(IFDIR);
		inossize(&dino, inp->i_isize);
		memcpy(&dino.e2di_blocks[0], &inp->i_blks[0], (size_t)inp->i_numblks);
		curino.id_number = inp->i_number;
		curino.id_parent = inp->i_parent;
		(void)ckinode(&dino, &curino);
	}
	/*
	 * Now that the parents of all directories have been found,
	 * make another pass to verify the value of `..'
	 */
	for (inpp = inpsort; inpp < inpend; inpp++) {
		inp = *inpp;
		if (inp->i_parent == 0 || inp->i_isize == 0)
			continue;
		if (inp->i_dotdot == inp->i_parent ||
		    inp->i_dotdot == (ino_t)-1)
			continue;
		if (inp->i_dotdot == 0) {
			inp->i_dotdot = inp->i_parent;
			fileerror(inp->i_parent, inp->i_number, "MISSING '..'");
			if (reply("FIX") == 0)
				continue;
			(void)makeentry(inp->i_number, inp->i_parent, "..");
			lncntp[inp->i_parent]--;
			continue;
		}
		fileerror(inp->i_parent, inp->i_number,
		    "BAD INODE NUMBER FOR '..'");
		if (reply("FIX") == 0)
			continue;
		lncntp[inp->i_dotdot]++;
		lncntp[inp->i_parent]--;
		inp->i_dotdot = inp->i_parent;
		(void)changeino(inp->i_number, "..", inp->i_parent);
	}
	/*
	 * Mark all the directories that can be found from the root.
	 */
	propagate();
}
Exemplo n.º 26
0
/*
 * Convert a component of a pathname into a pointer to a locked inode.
 * This is a very central and rather complicated routine.
 * If the file system is not maintained in a strict tree hierarchy,
 * this can result in a deadlock situation (see comments in code below).
 *
 * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending
 * on whether the name is to be looked up, created, renamed, or deleted.
 * When CREATE, RENAME, or DELETE is specified, information usable in
 * creating, renaming, or deleting a directory entry may be calculated.
 * If flag has LOCKPARENT or'ed into it and the target of the pathname
 * exists, lookup returns both the target and its parent directory locked.
 * When creating or renaming and LOCKPARENT is specified, the target may
 * not be ".".  When deleting and LOCKPARENT is specified, the target may
 * be "."., but the caller must check to ensure it does an vrele and vput
 * instead of two vputs.
 *
 * Overall outline of ext2fs_lookup:
 *
 *	check accessibility of directory
 *	look for name in cache, if found, then if at end of path
 *	  and deleting or creating, drop it, else return name
 *	search for name in directory, to found or notfound
 * notfound:
 *	if creating, return locked directory, leaving info on available slots
 *	else return error
 * found:
 *	if at end of path and deleting, return information to allow delete
 *	if at end of path and rewriting (RENAME and LOCKPARENT), lock target
 *	  inode and return info to allow rewrite
 *	if not at end, add name to cache; if at end and neither creating
 *	  nor deleting, add name to cache
 */
int
ext2fs_lookup(void *v)
{
	struct vop_lookup_args *ap = v;
	struct vnode *vdp;	/* vnode for directory being searched */
	struct inode *dp;	/* inode for directory being searched */
	struct buf *bp;			/* a buffer of directory entries */
	struct ext2fs_direct *ep; /* the current directory entry */
	int entryoffsetinblock;		/* offset of ep in bp's buffer */
	enum {NONE, COMPACT, FOUND} slotstatus;
	doff_t slotoffset;		/* offset of area with free space */
	int slotsize;			/* size of area at slotoffset */
	int slotfreespace;		/* amount of space free in slot */
	int slotneeded;			/* size of the entry we're seeking */
	int numdirpasses;		/* strategy for directory search */
	doff_t endsearch;		/* offset to end directory search */
	doff_t prevoff;			/* prev entry dp->i_offset */
	struct vnode *pdp;		/* saved dp during symlink work */
	struct vnode *tdp;		/* returned by VFS_VGET */
	doff_t enduseful;		/* pointer past last used dir slot */
	u_long bmask;			/* block offset mask */
	int lockparent;			/* 1 => lockparent flag is set */
	int wantparent;			/* 1 => wantparent or lockparent flag */
	int namlen, error;
	struct vnode **vpp = ap->a_vpp;
	struct componentname *cnp = ap->a_cnp;
	struct ucred *cred = cnp->cn_cred;
	int flags = cnp->cn_flags;
	int nameiop = cnp->cn_nameiop;
	struct proc *p = cnp->cn_proc;
	int	dirblksize = VTOI(ap->a_dvp)->i_e2fs->e2fs_bsize;

	bp = NULL;
	slotoffset = -1;
	*vpp = NULL;
	vdp = ap->a_dvp;
	dp = VTOI(vdp);
	lockparent = flags & LOCKPARENT;
	wantparent = flags & (LOCKPARENT|WANTPARENT);
	/*
	 * Check accessiblity of directory.
	 */
	if ((error = VOP_ACCESS(vdp, VEXEC, cred)) != 0)
		return (error);

	if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) &&
	    (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
		return (EROFS);

	/*
	 * We now have a segment name to search for, and a directory to search.
	 *
	 * Before tediously performing a linear scan of the directory,
	 * check the name cache to see if the directory/name pair
	 * we are looking for is known already.
	 */
	if ((error = cache_lookup(vdp, vpp, cnp)) >= 0)
		return (error);

	/*
	 * Suppress search for slots unless creating
	 * file and at end of pathname, in which case
	 * we watch for a place to put the new file in
	 * case it doesn't already exist.
	 */
	slotstatus = FOUND;
	slotfreespace = slotsize = slotneeded = 0;
	if ((nameiop == CREATE || nameiop == RENAME) &&
		(flags & ISLASTCN)) {
		slotstatus = NONE;
		slotneeded = EXT2FS_DIRSIZ(cnp->cn_namelen);
	}

	/*
	 * If there is cached information on a previous search of
	 * this directory, pick up where we last left off.
	 * We cache only lookups as these are the most common
	 * and have the greatest payoff. Caching CREATE has little
	 * benefit as it usually must search the entire directory
	 * to determine that the entry does not exist. Caching the
	 * location of the last DELETE or RENAME has not reduced
	 * profiling time and hence has been removed in the interest
	 * of simplicity.
	 */
	bmask = VFSTOUFS(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1;
	if (nameiop != LOOKUP || dp->i_diroff == 0 ||
		dp->i_diroff >ext2fs_size(dp)) {
		entryoffsetinblock = 0;
		dp->i_offset = 0;
		numdirpasses = 1;
	} else {
		dp->i_offset = dp->i_diroff;
		if ((entryoffsetinblock = dp->i_offset & bmask) &&
			(error = ext2fs_bufatoff(dp, (off_t)dp->i_offset, 
			    NULL, &bp)))
			return (error);
		numdirpasses = 2;
	}
	prevoff = dp->i_offset;
	endsearch = roundup(ext2fs_size(dp), dirblksize);
	enduseful = 0;

searchloop:
	while (dp->i_offset < endsearch) {
		/*
		 * If necessary, get the next directory block.
		 */
		if ((dp->i_offset & bmask) == 0) {
			if (bp != NULL)
				brelse(bp);
			error = ext2fs_bufatoff(dp, (off_t)dp->i_offset, 
			    NULL, &bp);
			if (error != 0)
				return (error);
			entryoffsetinblock = 0;
		}
		/*
		 * If still looking for a slot, and at a dirblksize
		 * boundary, have to start looking for free space again.
		 */
		if (slotstatus == NONE &&
			(entryoffsetinblock & (dirblksize - 1)) == 0) {
			slotoffset = -1;
			slotfreespace = 0;
		}
		/*
		 * Get pointer to next entry.
		 * Full validation checks are slow, so we only check
		 * enough to insure forward progress through the
		 * directory. Complete checks can be run by patching
		 * "dirchk" to be true.
		 */
		ep = (struct ext2fs_direct *)
			((char *)bp->b_data + entryoffsetinblock);
		if (ep->e2d_reclen == 0 ||
		    (dirchk &&
		    ext2fs_dirbadentry(vdp, ep, entryoffsetinblock))) {
			int i;
			ufs_dirbad(dp, dp->i_offset, "mangled entry");
			i = dirblksize -
			    (entryoffsetinblock & (dirblksize - 1));
			dp->i_offset += i;
			entryoffsetinblock += i;
			continue;
		}

		/*
		 * If an appropriate sized slot has not yet been found,
		 * check to see if one is available. Also accumulate space
		 * in the current block so that we can determine if
		 * compaction is viable.
		 */
		if (slotstatus != FOUND) {
			int size = fs2h16(ep->e2d_reclen);

			if (ep->e2d_ino != 0)
				size -= EXT2FS_DIRSIZ(ep->e2d_namlen);
			if (size > 0) {
				if (size >= slotneeded) {
					slotstatus = FOUND;
					slotoffset = dp->i_offset;
					slotsize = fs2h16(ep->e2d_reclen);
				} else if (slotstatus == NONE) {
					slotfreespace += size;
					if (slotoffset == -1)
						slotoffset = dp->i_offset;
					if (slotfreespace >= slotneeded) {
						slotstatus = COMPACT;
						slotsize = dp->i_offset +
							  fs2h16(ep->e2d_reclen) - slotoffset;
					}
				}
			}
		}

		/*
		 * Check for a name match.
		 */
		if (ep->e2d_ino) {
			namlen = ep->e2d_namlen;
			if (namlen == cnp->cn_namelen &&
				!memcmp(cnp->cn_nameptr, ep->e2d_name,
				(unsigned)namlen)) {
				/*
				 * Save directory entry's inode number and
				 * reclen in ndp->ni_ufs area, and release
				 * directory buffer.
				 */
				dp->i_ino = fs2h32(ep->e2d_ino);
				dp->i_reclen = fs2h16(ep->e2d_reclen);
				brelse(bp);
				goto found;
			}
		}
		prevoff = dp->i_offset;
		dp->i_offset += fs2h16(ep->e2d_reclen);
		entryoffsetinblock += fs2h16(ep->e2d_reclen);
		if (ep->e2d_ino)
			enduseful = dp->i_offset;
	}
/* notfound: */
	/*
	 * If we started in the middle of the directory and failed
	 * to find our target, we must check the beginning as well.
	 */
	if (numdirpasses == 2) {
		numdirpasses--;
		dp->i_offset = 0;
		endsearch = dp->i_diroff;
		goto searchloop;
	}
	if (bp != NULL)
		brelse(bp);
	/*
	 * If creating, and at end of pathname and current
	 * directory has not been removed, then can consider
	 * allowing file to be created.
	 */
	if ((nameiop == CREATE || nameiop == RENAME) &&
		(flags & ISLASTCN) && dp->i_e2fs_nlink != 0) {
		/*
		 * Creation of files on a read-only mounted file system
		 * is pointless, so don't proceed any further.
		 */
		if (vdp->v_mount->mnt_flag & MNT_RDONLY)
					return (EROFS);
		/*
		 * Access for write is interpreted as allowing
		 * creation of files in the directory.
		 */
		if ((error = VOP_ACCESS(vdp, VWRITE, cred)) != 0)
			return (error);
		/*
		 * Return an indication of where the new directory
		 * entry should be put.  If we didn't find a slot,
		 * then set dp->i_count to 0 indicating
		 * that the new slot belongs at the end of the
		 * directory. If we found a slot, then the new entry
		 * can be put in the range from dp->i_offset to
		 * dp->i_offset + dp->i_count.
		 */
		if (slotstatus == NONE) {
			dp->i_offset = roundup(ext2fs_size(dp), dirblksize);
			dp->i_count = 0;
			enduseful = dp->i_offset;
		} else {
			dp->i_offset = slotoffset;
			dp->i_count = slotsize;
			if (enduseful < slotoffset + slotsize)
				enduseful = slotoffset + slotsize;
		}
		dp->i_endoff = roundup(enduseful, dirblksize);
		dp->i_flag |= IN_CHANGE | IN_UPDATE;
		/*
		 * We return with the directory locked, so that
		 * the parameters we set up above will still be
		 * valid if we actually decide to do a direnter().
		 * We return ni_vp == NULL to indicate that the entry
		 * does not currently exist; we leave a pointer to
		 * the (locked) directory inode in ndp->ni_dvp.
		 * The pathname buffer is saved so that the name
		 * can be obtained later.
		 *
		 * NB - if the directory is unlocked, then this
		 * information cannot be used.
		 */
		cnp->cn_flags |= SAVENAME;
		if (!lockparent) {
			VOP_UNLOCK(vdp, 0);
			cnp->cn_flags |= PDIRUNLOCK;
		}
		return (EJUSTRETURN);
	}
	/*
	 * Insert name into cache (as non-existent) if appropriate.
	 */
	if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)
		cache_enter(vdp, *vpp, cnp);
	return (ENOENT);

found:
	/*
	 * Check that directory length properly reflects presence
	 * of this entry.
	 */
	if (entryoffsetinblock + EXT2FS_DIRSIZ(ep->e2d_namlen)
	    > ext2fs_size(dp)) {
		ufs_dirbad(dp, dp->i_offset, "i_size too small");
		error = ext2fs_setsize(dp,
			entryoffsetinblock + EXT2FS_DIRSIZ(ep->e2d_namlen));
		if (error) {
			brelse(bp);
			return(error);
		}
		dp->i_flag |= IN_CHANGE | IN_UPDATE;
	}

	/*
	 * Found component in pathname.
	 * If the final component of path name, save information
	 * in the cache as to where the entry was found.
	 */
	if ((flags & ISLASTCN) && nameiop == LOOKUP)
		dp->i_diroff = dp->i_offset &~ (dirblksize - 1);

	/*
	 * If deleting, and at end of pathname, return
	 * parameters which can be used to remove file.
	 * If the wantparent flag isn't set, we return only
	 * the directory (in ndp->ni_dvp), otherwise we go
	 * on and lock the inode, being careful with ".".
	 */
	if (nameiop == DELETE && (flags & ISLASTCN)) {
		/*
		 * Write access to directory required to delete files.
		 */
		if ((error = VOP_ACCESS(vdp, VWRITE, cred)) != 0)
			return (error);
		/*
		 * Return pointer to current entry in dp->i_offset,
		 * and distance past previous entry (if there
		 * is a previous entry in this block) in dp->i_count.
		 * Save directory inode pointer in ndp->ni_dvp for dirremove().
		 */
		if ((dp->i_offset & (dirblksize - 1)) == 0)
			dp->i_count = 0;
		else
			dp->i_count = dp->i_offset - prevoff;
		if (dp->i_number == dp->i_ino) {
			vref(vdp);
			*vpp = vdp;
			return (0);
		}
		if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0)
			return (error);
		/*
		 * If directory is "sticky", then user must own
		 * the directory, or the file in it, else she
		 * may not delete it (unless she's root). This
		 * implements append-only directories.
		 */
		if ((dp->i_e2fs_mode & ISVTX) &&
			cred->cr_uid != 0 &&
			cred->cr_uid != dp->i_e2fs_uid &&
			VTOI(tdp)->i_e2fs_uid != cred->cr_uid) {
			vput(tdp);
			return (EPERM);
		}
		*vpp = tdp;
		if (!lockparent) {
			VOP_UNLOCK(vdp, 0);
			cnp->cn_flags |= PDIRUNLOCK;
		}
		return (0);
	}

	/*
	 * If rewriting (RENAME), return the inode and the
	 * information required to rewrite the present directory
	 * Must get inode of directory entry to verify it's a
	 * regular file, or empty directory.
	 */
	if (nameiop == RENAME && wantparent &&
		(flags & ISLASTCN)) {
		if ((error = VOP_ACCESS(vdp, VWRITE, cred)) != 0)
			return (error);
		/*
		 * Careful about locking second inode.
		 * This can only occur if the target is ".".
		 */
		if (dp->i_number == dp->i_ino)
			return (EISDIR);
		if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0)
			return (error);
		*vpp = tdp;
		cnp->cn_flags |= SAVENAME;
		if (!lockparent) {
			VOP_UNLOCK(vdp, 0);
			cnp->cn_flags |= PDIRUNLOCK;
		}
		return (0);
	}

	/*
	 * Step through the translation in the name.  We do not `vput' the
	 * directory because we may need it again if a symbolic link
	 * is relative to the current directory.  Instead we save it
	 * unlocked as "pdp".  We must get the target inode before unlocking
	 * the directory to insure that the inode will not be removed
	 * before we get it.  We prevent deadlock by always fetching
	 * inodes from the root, moving down the directory tree. Thus
	 * when following backward pointers ".." we must unlock the
	 * parent directory before getting the requested directory.
	 * There is a potential race condition here if both the current
	 * and parent directories are removed before the VFS_VGET for the
	 * inode associated with ".." returns.  We hope that this occurs
	 * infrequently since we cannot avoid this race condition without
	 * implementing a sophisticated deadlock detection algorithm.
	 * Note also that this simple deadlock detection scheme will not
	 * work if the file system has any hard links other than ".."
	 * that point backwards in the directory structure.
	 */
	pdp = vdp;
	if (flags & ISDOTDOT) {
		VOP_UNLOCK(pdp, 0);	/* race to get the inode */
		cnp->cn_flags |= PDIRUNLOCK;
		if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0) {
			if (vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, p) == 0)
				cnp->cn_flags &= ~PDIRUNLOCK;
			return (error);
		}
		if (lockparent && (flags & ISLASTCN)) {
			if ((error = vn_lock(pdp, LK_EXCLUSIVE, p)) != 0) {
				vput(tdp);
				return (error);
			}
			cnp->cn_flags &= ~PDIRUNLOCK;
		}
		*vpp = tdp;
	} else if (dp->i_number == dp->i_ino) {
		vref(vdp);	/* we want ourself, ie "." */
		*vpp = vdp;
	} else {
		if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0)
			return (error);
		if (!lockparent || !(flags & ISLASTCN)) {
			VOP_UNLOCK(pdp, 0);
			cnp->cn_flags |= PDIRUNLOCK;
		}
		*vpp = tdp;
	}

	/*
	 * Insert name into cache if appropriate.
	 */
	if (cnp->cn_flags & MAKEENTRY)
		cache_enter(vdp, *vpp, cnp);
	return (0);
}
Exemplo n.º 27
0
int
setup(const char *dev)
{
	long cg, asked, i;
	long bmapsize;
	struct disklabel *lp;
	off_t sizepb;
	struct stat statb;
	struct m_ext2fs proto;
	int doskipclean;
	u_int64_t maxfilesize;

	havesb = 0;
	fswritefd = -1;
	doskipclean = skipclean;
	if (stat(dev, &statb) < 0) {
		printf("Can't stat %s: %s\n", dev, strerror(errno));
		return 0;
	}
	if (!S_ISCHR(statb.st_mode)) {
		pfatal("%s is not a character device", dev);
		if (reply("CONTINUE") == 0)
			return 0;
	}
	if ((fsreadfd = open(dev, O_RDONLY)) < 0) {
		printf("Can't open %s: %s\n", dev, strerror(errno));
		return 0;
	}
	if (preen == 0)
		printf("** %s", dev);
	if (nflag || (fswritefd = open(dev, O_WRONLY)) < 0) {
		fswritefd = -1;
		if (preen)
			pfatal("NO WRITE ACCESS");
		printf(" (NO WRITE)");
	}
	if (preen == 0)
		printf("\n");
	fsmodified = 0;
	lfdir = 0;
	initbarea(&sblk);
	initbarea(&asblk);
	sblk.b_un.b_buf = malloc(SBSIZE);
	asblk.b_un.b_buf = malloc(SBSIZE);
	if (sblk.b_un.b_buf == NULL || asblk.b_un.b_buf == NULL)
		errexit("cannot allocate space for superblock");
	if ((lp = getdisklabel(NULL, fsreadfd)) != NULL)
		dev_bsize = secsize = lp->d_secsize;
	else
		dev_bsize = secsize = DEV_BSIZE;
	/*
	 * Read in the superblock, looking for alternates if necessary
	 */
	if (readsb(1) == 0) {
		if (bflag || preen || calcsb(dev, fsreadfd, &proto) == 0)
			return 0;
		if (reply("LOOK FOR ALTERNATE SUPERBLOCKS") == 0)
			return 0;
		for (cg = 1; cg < proto.e2fs_ncg; cg++) {
			bflag = EXT2_FSBTODB(&proto,
			    cg * proto.e2fs.e2fs_bpg +
			    proto.e2fs.e2fs_first_dblock);
			if (readsb(0) != 0)
				break;
		}
		if (cg >= proto.e2fs_ncg) {
			printf("%s %s\n%s %s\n%s %s\n",
			    "SEARCH FOR ALTERNATE SUPER-BLOCK",
			    "FAILED. YOU MUST USE THE",
			    "-b OPTION TO FSCK_FFS TO SPECIFY THE",
			    "LOCATION OF AN ALTERNATE",
			    "SUPER-BLOCK TO SUPPLY NEEDED",
			    "INFORMATION; SEE fsck_ext2fs(8).");
			return 0;
		}
		doskipclean = 0;
		pwarn("USING ALTERNATE SUPERBLOCK AT %d\n", bflag);
	}
	if (debug)
		printf("state = %d\n", sblock.e2fs.e2fs_state);
	if (sblock.e2fs.e2fs_state == E2FS_ISCLEAN) {
		if (doskipclean) {
			pwarn("%sile system is clean; not checking\n",
			    preen ? "f" : "** F");
			return -1;
		}
		if (!preen)
			pwarn("** File system is already clean\n");
	}
	maxfsblock = sblock.e2fs.e2fs_bcount;
	maxino = sblock.e2fs_ncg * sblock.e2fs.e2fs_ipg;
	sizepb = sblock.e2fs_bsize;
	maxfilesize = sblock.e2fs_bsize * EXT2FS_NDADDR - 1;
	for (i = 0; i < EXT2FS_NIADDR; i++) {
		sizepb *= EXT2_NINDIR(&sblock);
		maxfilesize += sizepb;
	}
	/*
	 * Check and potentially fix certain fields in the super block.
	 */
	if (/* (sblock.e2fs.e2fs_rbcount < 0) || */
	    (sblock.e2fs.e2fs_rbcount > sblock.e2fs.e2fs_bcount)) {
		pfatal("IMPOSSIBLE RESERVED BLOCK COUNT=%d IN SUPERBLOCK",
		    sblock.e2fs.e2fs_rbcount);
		if (reply("SET TO DEFAULT") == 1) {
			sblock.e2fs.e2fs_rbcount =
			    sblock.e2fs.e2fs_bcount * MINFREE / 100;
			sbdirty();
			dirty(&asblk);
		}
	}
	if (sblock.e2fs.e2fs_bpg != sblock.e2fs.e2fs_fpg) {
		pfatal("WRONG FPG=%d (BPG=%d) IN SUPERBLOCK",
		    sblock.e2fs.e2fs_fpg, sblock.e2fs.e2fs_bpg);
		return 0;
	}
	if (asblk.b_dirty && !bflag) {
		copyback_sb(&asblk);
		flush(fswritefd, &asblk);
	}
	/*
	 * read in the summary info.
	 */

	sblock.e2fs_gd = malloc(sblock.e2fs_ngdb * sblock.e2fs_bsize);
	if (sblock.e2fs_gd == NULL)
		errexit("out of memory");
	asked = 0;
	for (i = 0; i < sblock.e2fs_ngdb; i++) {
		if (bread(fsreadfd,
		    (char *)&sblock.e2fs_gd[i * sblock.e2fs_bsize /
		    sizeof(struct ext2_gd)],
		    EXT2_FSBTODB(&sblock, ((sblock.e2fs_bsize > 1024) ? 0 : 1) +
		    i + 1),
		    sblock.e2fs_bsize) != 0 && !asked) {
			pfatal("BAD SUMMARY INFORMATION");
			if (reply("CONTINUE") == 0)
				exit(FSCK_EXIT_CHECK_FAILED);
			asked++;
		}
	}
	/*
	 * allocate and initialize the necessary maps
	 */
	bmapsize = roundup(howmany(maxfsblock, NBBY), sizeof(int16_t));
	blockmap = calloc((unsigned int)bmapsize, sizeof(char));
	if (blockmap == NULL) {
		printf("cannot alloc %u bytes for blockmap\n",
		    (unsigned int)bmapsize);
		goto badsblabel;
	}
	statemap = calloc((unsigned int)(maxino + 2), sizeof(char));
	if (statemap == NULL) {
		printf("cannot alloc %u bytes for statemap\n",
		    (unsigned int)(maxino + 1));
		goto badsblabel;
	}
	typemap = calloc((unsigned int)(maxino + 1), sizeof(char));
	if (typemap == NULL) {
		printf("cannot alloc %u bytes for typemap\n",
		    (unsigned int)(maxino + 1));
		goto badsblabel;
	}
	lncntp = calloc((unsigned)(maxino + 1), sizeof(int16_t));
	if (lncntp == NULL) {
		printf("cannot alloc %u bytes for lncntp\n",
		    (unsigned int)((maxino + 1) * sizeof(int16_t)));
		goto badsblabel;
	}
	for (numdirs = 0, cg = 0; cg < sblock.e2fs_ncg; cg++) {
		numdirs += fs2h16(sblock.e2fs_gd[cg].ext2bgd_ndirs);
	}
	inplast = 0;
	listmax = numdirs + 10;
	inpsort = calloc((unsigned int)listmax, sizeof(struct inoinfo *));
	inphead = calloc((unsigned int)numdirs, sizeof(struct inoinfo *));
	if (inpsort == NULL || inphead == NULL) {
		printf("cannot alloc %u bytes for inphead\n",
		    (unsigned int)(numdirs * sizeof(struct inoinfo *)));
		goto badsblabel;
	}
	bufinit();
	return 1;

badsblabel:
	ckfini(0);
	return 0;
}
/*
 * Convert a component of a pathname into a pointer to a locked inode.
 * This is a very central and rather complicated routine.
 * If the file system is not maintained in a strict tree hierarchy,
 * this can result in a deadlock situation (see comments in code below).
 *
 * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending
 * on whether the name is to be looked up, created, renamed, or deleted.
 * When CREATE, RENAME, or DELETE is specified, information usable in
 * creating, renaming, or deleting a directory entry may be calculated.
 * If flag has LOCKPARENT or'ed into it and the target of the pathname
 * exists, lookup returns both the target and its parent directory locked.
 * When creating or renaming and LOCKPARENT is specified, the target may
 * not be ".".  When deleting and LOCKPARENT is specified, the target may
 * be "."., but the caller must check to ensure it does an vrele and vput
 * instead of two vputs.
 *
 * Overall outline of ext2fs_lookup:
 *
 *	check accessibility of directory
 *	look for name in cache, if found, then if at end of path
 *	  and deleting or creating, drop it, else return name
 *	search for name in directory, to found or notfound
 * notfound:
 *	if creating, return locked directory, leaving info on available slots
 *	else return error
 * found:
 *	if at end of path and deleting, return information to allow delete
 *	if at end of path and rewriting (RENAME and LOCKPARENT), lock target
 *	  inode and return info to allow rewrite
 *	if not at end, add name to cache; if at end and neither creating
 *	  nor deleting, add name to cache
 */
int
ext2fs_lookup(void *v)
{
	struct vop_lookup_v2_args /* {
		struct vnode *a_dvp;
		struct vnode **a_vpp;
		struct componentname *a_cnp;
	} */ *ap = v;
	struct vnode *vdp = ap->a_dvp;	/* vnode for directory being searched */
	struct inode *dp = VTOI(vdp);	/* inode for directory being searched */
	struct buf *bp;			/* a buffer of directory entries */
	struct ext2fs_direct *ep;	/* the current directory entry */
	int entryoffsetinblock;		/* offset of ep in bp's buffer */
	enum {NONE, COMPACT, FOUND} slotstatus;
	doff_t slotoffset;		/* offset of area with free space */
	int slotsize;			/* size of area at slotoffset */
	int slotfreespace;		/* amount of space free in slot */
	int slotneeded;			/* size of the entry we're seeking */
	int numdirpasses;		/* strategy for directory search */
	doff_t endsearch;		/* offset to end directory search */
	doff_t prevoff;			/* prev entry dp->i_offset */
	struct vnode *tdp;		/* returned by vcache_get */
	doff_t enduseful;		/* pointer past last used dir slot */
	u_long bmask;			/* block offset mask */
	int namlen, error;
	struct vnode **vpp = ap->a_vpp;
	struct componentname *cnp = ap->a_cnp;
	kauth_cred_t cred = cnp->cn_cred;
	int flags;
	int nameiop = cnp->cn_nameiop;
	struct ufsmount *ump = dp->i_ump;
	int dirblksiz = ump->um_dirblksiz;
	ino_t foundino;
	struct ufs_lookup_results *results;

	flags = cnp->cn_flags;

	bp = NULL;
	slotoffset = -1;
	*vpp = NULL;

	/*
	 * Produce the auxiliary lookup results into i_crap. Increment
	 * its serial number so elsewhere we can tell if we're using
	 * stale results. This should not be done this way. XXX.
	 */
	results = &dp->i_crap;
	dp->i_crapcounter++;

	/*
	 * Check accessiblity of directory.
	 */
	if ((error = VOP_ACCESS(vdp, VEXEC, cred)) != 0)
		return (error);

	if ((flags & ISLASTCN) && (vdp->v_mount->mnt_flag & MNT_RDONLY) &&
	    (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
		return (EROFS);

	/*
	 * We now have a segment name to search for, and a directory to search.
	 *
	 * Before tediously performing a linear scan of the directory,
	 * check the name cache to see if the directory/name pair
	 * we are looking for is known already.
	 */
	if (cache_lookup(vdp, cnp->cn_nameptr, cnp->cn_namelen,
			 cnp->cn_nameiop, cnp->cn_flags, NULL, vpp)) {
		return *vpp == NULLVP ? ENOENT : 0;
	}

	/*
	 * Suppress search for slots unless creating
	 * file and at end of pathname, in which case
	 * we watch for a place to put the new file in
	 * case it doesn't already exist.
	 */
	slotstatus = FOUND;
	slotfreespace = slotsize = slotneeded = 0;
	if ((nameiop == CREATE || nameiop == RENAME) &&
	    (flags & ISLASTCN)) {
		slotstatus = NONE;
		slotneeded = EXT2FS_DIRSIZ(cnp->cn_namelen);
	}

	/*
	 * If there is cached information on a previous search of
	 * this directory, pick up where we last left off.
	 * We cache only lookups as these are the most common
	 * and have the greatest payoff. Caching CREATE has little
	 * benefit as it usually must search the entire directory
	 * to determine that the entry does not exist. Caching the
	 * location of the last DELETE or RENAME has not reduced
	 * profiling time and hence has been removed in the interest
	 * of simplicity.
	 */
	bmask = vdp->v_mount->mnt_stat.f_iosize - 1;
	if (nameiop != LOOKUP || results->ulr_diroff == 0 ||
	    results->ulr_diroff >= ext2fs_size(dp)) {
		entryoffsetinblock = 0;
		results->ulr_offset = 0;
		numdirpasses = 1;
	} else {
		results->ulr_offset = results->ulr_diroff;
		if ((entryoffsetinblock = results->ulr_offset & bmask) &&
		    (error = ext2fs_blkatoff(vdp, (off_t)results->ulr_offset, NULL, &bp)))
			return (error);
		numdirpasses = 2;
		namecache_count_2passes();
	}
	prevoff = results->ulr_offset;
	endsearch = roundup(ext2fs_size(dp), dirblksiz);
	enduseful = 0;

searchloop:
	while (results->ulr_offset < endsearch) {
		if (curcpu()->ci_schedstate.spc_flags & SPCF_SHOULDYIELD)
			preempt();
		/*
		 * If necessary, get the next directory block.
		 */
		if ((results->ulr_offset & bmask) == 0) {
			if (bp != NULL)
				brelse(bp, 0);
			error = ext2fs_blkatoff(vdp, (off_t)results->ulr_offset, NULL,
			    &bp);
			if (error != 0)
				return (error);
			entryoffsetinblock = 0;
		}
		/*
		 * If still looking for a slot, and at a dirblksize
		 * boundary, have to start looking for free space again.
		 */
		if (slotstatus == NONE &&
		    (entryoffsetinblock & (dirblksiz - 1)) == 0) {
			slotoffset = -1;
			slotfreespace = 0;
		}
		/*
		 * Get pointer to next entry.
		 * Full validation checks are slow, so we only check
		 * enough to insure forward progress through the
		 * directory. Complete checks can be run by patching
		 * "dirchk" to be true.
		 */
		KASSERT(bp != NULL);
		ep = (struct ext2fs_direct *)
			((char *)bp->b_data + entryoffsetinblock);
		if (ep->e2d_reclen == 0 ||
		    (dirchk &&
		     ext2fs_dirbadentry(vdp, ep, entryoffsetinblock))) {
			int i;

			ufs_dirbad(dp, results->ulr_offset, "mangled entry");
			i = dirblksiz - (entryoffsetinblock & (dirblksiz - 1));
			results->ulr_offset += i;
			entryoffsetinblock += i;
			continue;
		}

		/*
		 * If an appropriate sized slot has not yet been found,
		 * check to see if one is available. Also accumulate space
		 * in the current block so that we can determine if
		 * compaction is viable.
		 */
		if (slotstatus != FOUND) {
			int size = fs2h16(ep->e2d_reclen);

			if (ep->e2d_ino != 0)
				size -= EXT2FS_DIRSIZ(ep->e2d_namlen);
			if (size > 0) {
				if (size >= slotneeded) {
					slotstatus = FOUND;
					slotoffset = results->ulr_offset;
					slotsize = fs2h16(ep->e2d_reclen);
				} else if (slotstatus == NONE) {
					slotfreespace += size;
					if (slotoffset == -1)
						slotoffset = results->ulr_offset;
					if (slotfreespace >= slotneeded) {
						slotstatus = COMPACT;
						slotsize = results->ulr_offset +
						    fs2h16(ep->e2d_reclen) -
						    slotoffset;
					}
				}
			}
		}

		/*
		 * Check for a name match.
		 */
		if (ep->e2d_ino) {
			namlen = ep->e2d_namlen;
			if (namlen == cnp->cn_namelen &&
			    !memcmp(cnp->cn_nameptr, ep->e2d_name,
			    (unsigned)namlen)) {
				/*
				 * Save directory entry's inode number and
				 * reclen in ndp->ni_ufs area, and release
				 * directory buffer.
				 */
				foundino = fs2h32(ep->e2d_ino);
				results->ulr_reclen = fs2h16(ep->e2d_reclen);
				goto found;
			}
		}
		prevoff = results->ulr_offset;
		results->ulr_offset += fs2h16(ep->e2d_reclen);
		entryoffsetinblock += fs2h16(ep->e2d_reclen);
		if (ep->e2d_ino)
			enduseful = results->ulr_offset;
	}
/* notfound: */
	/*
	 * If we started in the middle of the directory and failed
	 * to find our target, we must check the beginning as well.
	 */
	if (numdirpasses == 2) {
		numdirpasses--;
		results->ulr_offset = 0;
		endsearch = results->ulr_diroff;
		goto searchloop;
	}
	if (bp != NULL)
		brelse(bp, 0);
	/*
	 * If creating, and at end of pathname and current
	 * directory has not been removed, then can consider
	 * allowing file to be created.
	 */
	if ((nameiop == CREATE || nameiop == RENAME) &&
	    (flags & ISLASTCN) && dp->i_e2fs_nlink != 0) {
		/*
		 * Access for write is interpreted as allowing
		 * creation of files in the directory.
		 */
		error = VOP_ACCESS(vdp, VWRITE, cred);
		if (error)
			return (error);
		/*
		 * Return an indication of where the new directory
		 * entry should be put.  If we didn't find a slot,
		 * then set results->ulr_count to 0 indicating
		 * that the new slot belongs at the end of the
		 * directory. If we found a slot, then the new entry
		 * can be put in the range from results->ulr_offset to
		 * results->ulr_offset + results->ulr_count.
		 */
		if (slotstatus == NONE) {
			results->ulr_offset = roundup(ext2fs_size(dp), dirblksiz);
			results->ulr_count = 0;
			enduseful = results->ulr_offset;
		} else {
			results->ulr_offset = slotoffset;
			results->ulr_count = slotsize;
			if (enduseful < slotoffset + slotsize)
				enduseful = slotoffset + slotsize;
		}
		results->ulr_endoff = roundup(enduseful, dirblksiz);
#if 0
		dp->i_flag |= IN_CHANGE | IN_UPDATE;
#endif
		/*
		 * We return with the directory locked, so that
		 * the parameters we set up above will still be
		 * valid if we actually decide to do a direnter().
		 * We return ni_vp == NULL to indicate that the entry
		 * does not currently exist; we leave a pointer to
		 * the (locked) directory inode in ndp->ni_dvp.
		 *
		 * NB - if the directory is unlocked, then this
		 * information cannot be used.
		 */
		return (EJUSTRETURN);
	}
	/*
	 * Insert name into cache (as non-existent) if appropriate.
	 */
	if (nameiop != CREATE) {
		cache_enter(vdp, *vpp, cnp->cn_nameptr, cnp->cn_namelen,
			    cnp->cn_flags);
	}
	return ENOENT;

found:
	if (numdirpasses == 2)
		namecache_count_pass2();
	/*
	 * Check that directory length properly reflects presence
	 * of this entry.
	 */
	if (results->ulr_offset + EXT2FS_DIRSIZ(ep->e2d_namlen) > ext2fs_size(dp)) {
		ufs_dirbad(dp, results->ulr_offset, "i_size too small");
		error = ext2fs_setsize(dp,
				results->ulr_offset + EXT2FS_DIRSIZ(ep->e2d_namlen));
		if (error) {
			brelse(bp, 0);
			return (error);
		}
		dp->i_flag |= IN_CHANGE | IN_UPDATE;
		uvm_vnp_setsize(vdp, ext2fs_size(dp));
	}
	brelse(bp, 0);

	/*
	 * Found component in pathname.
	 * If the final component of path name, save information
	 * in the cache as to where the entry was found.
	 */
	if ((flags & ISLASTCN) && nameiop == LOOKUP)
		results->ulr_diroff = results->ulr_offset &~ (dirblksiz - 1);

	/*
	 * If deleting, and at end of pathname, return
	 * parameters which can be used to remove file.
	 * Lock the inode, being careful with ".".
	 */
	if (nameiop == DELETE && (flags & ISLASTCN)) {
		/*
		 * Return pointer to current entry in results->ulr_offset,
		 * and distance past previous entry (if there
		 * is a previous entry in this block) in results->ulr_count.
		 * Save directory inode pointer in ndp->ni_dvp for dirremove().
		 */
		if ((results->ulr_offset & (dirblksiz - 1)) == 0)
			results->ulr_count = 0;
		else
			results->ulr_count = results->ulr_offset - prevoff;
		if (dp->i_number == foundino) {
			vref(vdp);
			tdp = vdp;
		} else {
			error = vcache_get(vdp->v_mount,
			    &foundino, sizeof(foundino), &tdp);
			if (error)
				return (error);
		}
		/*
		 * Write access to directory required to delete files.
		 */
		if ((error = VOP_ACCESS(vdp, VWRITE, cred)) != 0) {
			vrele(tdp);
			return (error);
		}
		/*
		 * If directory is "sticky", then user must own
		 * the directory, or the file in it, else she
		 * may not delete it (unless she's root). This
		 * implements append-only directories.
		 */
		if (dp->i_e2fs_mode & ISVTX) {
			error = kauth_authorize_vnode(cred, KAUTH_VNODE_DELETE,
			    tdp, vdp, genfs_can_sticky(cred, dp->i_uid,
			    VTOI(tdp)->i_uid));
			if (error) {
				vrele(tdp);
				return (EPERM);
			}
		}
		*vpp = tdp;
		return (0);
	}

	/*
	 * If rewriting (RENAME), return the inode and the
	 * information required to rewrite the present directory
	 * Must get inode of directory entry to verify it's a
	 * regular file, or empty directory.
	 */
	if (nameiop == RENAME && (flags & ISLASTCN)) {
		error = VOP_ACCESS(vdp, VWRITE, cred);
		if (error)
			return (error);
		/*
		 * Careful about locking second inode.
		 * This can only occur if the target is ".".
		 */
		if (dp->i_number == foundino)
			return (EISDIR);
		error = vcache_get(vdp->v_mount,
		    &foundino, sizeof(foundino), &tdp);
		if (error)
			return (error);
		*vpp = tdp;
		return (0);
	}

	if (dp->i_number == foundino) {
		vref(vdp);	/* we want ourself, ie "." */
		*vpp = vdp;
	} else {
		error = vcache_get(vdp->v_mount,
		    &foundino, sizeof(foundino), &tdp);
		if (error)
			return (error);
		*vpp = tdp;
	}

	/*
	 * Insert name into cache if appropriate.
	 */
	cache_enter(vdp, *vpp, cnp->cn_nameptr, cnp->cn_namelen, cnp->cn_flags);
	return 0;
}
/*
 * Write a directory entry after a call to namei, using the parameters
 * that it left in nameidata.  The argument ip is the inode which the new
 * directory entry will refer to.  Dvp is a pointer to the directory to
 * be written, which was left locked by namei. Remaining parameters
 * (ulr_offset, ulr_count) indicate how the space for the new
 * entry is to be obtained.
 */
int
ext2fs_direnter(struct inode *ip, struct vnode *dvp,
		const struct ufs_lookup_results *ulr,
		struct componentname *cnp)
{
	struct ext2fs_direct *ep, *nep;
	struct inode *dp;
	struct buf *bp;
	struct ext2fs_direct newdir;
	struct iovec aiov;
	struct uio auio;
	u_int dsize;
	int error, loc, newentrysize, spacefree;
	char *dirbuf;
	struct ufsmount *ump = VFSTOUFS(dvp->v_mount);
	int dirblksiz = ump->um_dirblksiz;

	dp = VTOI(dvp);

	newdir.e2d_ino = h2fs32(ip->i_number);
	newdir.e2d_namlen = cnp->cn_namelen;
	if (ip->i_e2fs->e2fs.e2fs_rev > E2FS_REV0 &&
	    (ip->i_e2fs->e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) {
		newdir.e2d_type = inot2ext2dt(IFTODT(ip->i_e2fs_mode));
	} else {
		newdir.e2d_type = 0;
	}
	memcpy(newdir.e2d_name, cnp->cn_nameptr, (unsigned)cnp->cn_namelen + 1);
	newentrysize = EXT2FS_DIRSIZ(cnp->cn_namelen);
	if (ulr->ulr_count == 0) {
		/*
		 * If ulr_count is 0, then namei could find no
		 * space in the directory. Here, ulr_offset will
		 * be on a directory block boundary and we will write the
		 * new entry into a fresh block.
		 */
		if (ulr->ulr_offset & (dirblksiz - 1))
			panic("ext2fs_direnter: newblk");
		auio.uio_offset = ulr->ulr_offset;
		newdir.e2d_reclen = h2fs16(dirblksiz);
		auio.uio_resid = newentrysize;
		aiov.iov_len = newentrysize;
		aiov.iov_base = (void *)&newdir;
		auio.uio_iov = &aiov;
		auio.uio_iovcnt = 1;
		auio.uio_rw = UIO_WRITE;
		UIO_SETUP_SYSSPACE(&auio);
		error = VOP_WRITE(dvp, &auio, IO_SYNC, cnp->cn_cred);
		if (dirblksiz > dvp->v_mount->mnt_stat.f_bsize)
			/* XXX should grow with balloc() */
			panic("ext2fs_direnter: frag size");
		else if (!error) {
			error = ext2fs_setsize(dp,
				roundup(ext2fs_size(dp), dirblksiz));
			if (error)
				return (error);
			dp->i_flag |= IN_CHANGE;
			uvm_vnp_setsize(dvp, ext2fs_size(dp));
		}
		return (error);
	}

	/*
	 * If ulr_count is non-zero, then namei found space
	 * for the new entry in the range ulr_offset to
	 * ulr_offset + ulr_count in the directory.
	 * To use this space, we may have to compact the entries located
	 * there, by copying them together towards the beginning of the
	 * block, leaving the free space in one usable chunk at the end.
	 */

	/*
	 * Get the block containing the space for the new directory entry.
	 */
	if ((error = ext2fs_blkatoff(dvp, (off_t)ulr->ulr_offset, &dirbuf, &bp)) != 0)
		return (error);
	/*
	 * Find space for the new entry. In the simple case, the entry at
	 * offset base will have the space. If it does not, then namei
	 * arranged that compacting the region ulr_offset to
	 * ulr_offset + ulr_count would yield the
	 * space.
	 */
	ep = (struct ext2fs_direct *)dirbuf;
	dsize = EXT2FS_DIRSIZ(ep->e2d_namlen);
	spacefree = fs2h16(ep->e2d_reclen) - dsize;
	for (loc = fs2h16(ep->e2d_reclen); loc < ulr->ulr_count; ) {
		nep = (struct ext2fs_direct *)(dirbuf + loc);
		if (ep->e2d_ino) {
			/* trim the existing slot */
			ep->e2d_reclen = h2fs16(dsize);
			ep = (struct ext2fs_direct *)((char *)ep + dsize);
		} else {
			/* overwrite; nothing there; header is ours */
			spacefree += dsize;
		}
		dsize = EXT2FS_DIRSIZ(nep->e2d_namlen);
		spacefree += fs2h16(nep->e2d_reclen) - dsize;
		loc += fs2h16(nep->e2d_reclen);
		memcpy((void *)ep, (void *)nep, dsize);
	}
	/*
	 * Update the pointer fields in the previous entry (if any),
	 * copy in the new entry, and write out the block.
	 */
	if (ep->e2d_ino == 0) {
#ifdef DIAGNOSTIC
		if (spacefree + dsize < newentrysize)
			panic("ext2fs_direnter: compact1");
#endif
		newdir.e2d_reclen = h2fs16(spacefree + dsize);
	} else {
#ifdef DIAGNOSTIC
		if (spacefree < newentrysize) {
			printf("ext2fs_direnter: compact2 %u %u",
			    (u_int)spacefree, (u_int)newentrysize);
			panic("ext2fs_direnter: compact2");
		}
#endif
		newdir.e2d_reclen = h2fs16(spacefree);
		ep->e2d_reclen = h2fs16(dsize);
		ep = (struct ext2fs_direct *)((char *)ep + dsize);
	}
	memcpy((void *)ep, (void *)&newdir, (u_int)newentrysize);
	error = VOP_BWRITE(bp->b_vp, bp);
	dp->i_flag |= IN_CHANGE | IN_UPDATE;
	if (!error && ulr->ulr_endoff && ulr->ulr_endoff < ext2fs_size(dp))
		error = ext2fs_truncate(dvp, (off_t)ulr->ulr_endoff, IO_SYNC,
		    cnp->cn_cred);
	return (error);
}
Exemplo n.º 30
0
__compactcall void
ext2fs_ls(struct open_file *f, const char *pattern)
{
	struct file *fp = (struct file *)f->f_fsdata;
	size_t block_size = fp->f_fs->e2fs_bsize;
	char *buf;
	size_t buf_size;
	entry_t	*names = 0, *n, **np;

	fp->f_seekp = 0;
	while (fp->f_seekp < (off_t)fp->f_di.e2di_size) {
		struct ext2fs_direct  *dp, *edp;
		int rc = buf_read_file(f, &buf, &buf_size);
		if (rc)
			goto out;
		if (buf_size != block_size || buf_size == 0)
			goto out;

		dp = (struct ext2fs_direct *)buf;
		edp = (struct ext2fs_direct *)(buf + buf_size);

		for (; dp < edp;
		     dp = (void *)((char *)dp + fs2h16(dp->e2d_reclen))) {
			const char *t;

			if (fs2h16(dp->e2d_reclen) <= 0)
				goto out;

			if (fs2h32(dp->e2d_ino) == 0)
				continue;

			if (dp->e2d_type >= NELEM(typestr) ||
			    !(t = typestr[dp->e2d_type])) {
				/*
				 * This does not handle "old"
				 * filesystems properly. On little
				 * endian machines, we get a bogus
				 * type name if the namlen matches a
				 * valid type identifier. We could
				 * check if we read namlen "0" and
				 * handle this case specially, if
				 * there were a pressing need...
				 */
				printf("bad dir entry\n");
				goto out;
			}
			if (pattern && !fnmatch(dp->e2d_name, pattern))
				continue;
			n = alloc(sizeof *n + strlen(dp->e2d_name));
			if (!n) {
				printf("%d: %s (%s)\n",
					fs2h32(dp->e2d_ino), dp->e2d_name, t);
				continue;
			}
			n->e_ino = fs2h32(dp->e2d_ino);
			n->e_type = dp->e2d_type;
			strcpy(n->e_name, dp->e2d_name);
			for (np = &names; *np; np = &(*np)->e_next) {
				if (strcmp(n->e_name, (*np)->e_name) < 0)
					break;
			}
			n->e_next = *np;
			*np = n;
		}
		fp->f_seekp += buf_size;
	}

	if (names) {
		entry_t *p_names = names;
		do {
			n = p_names;
			printf("%d: %s (%s)\n",
				n->e_ino, n->e_name, typestr[n->e_type]);
			p_names = n->e_next;
		} while (p_names);
	} else {
		printf("not found\n");
	}
out:
	if (names) {
		do {
			n = names;
			names = n->e_next;
			dealloc(n, 0);
		} while (names);
	}
	return;
}