xref: /openbmc/linux/fs/ufs/dir.c (revision 4b6f5d20b04dcbc3d888555522b90ba6d36c4106)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *  linux/fs/ufs/ufs_dir.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Copyright (C) 1996
51da177e4SLinus Torvalds  * Adrian Rodriguez (adrian@franklins-tower.rutgers.edu)
61da177e4SLinus Torvalds  * Laboratory for Computer Science Research Computing Facility
71da177e4SLinus Torvalds  * Rutgers, The State University of New Jersey
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  * swab support by Francois-Rene Rideau <fare@tunes.org> 19970406
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  * 4.4BSD (FreeBSD) support added on February 1st 1998 by
121da177e4SLinus Torvalds  * Niels Kristian Bech Jensen <nkbj@image.dk> partially based
131da177e4SLinus Torvalds  * on code by Martin von Loewis <martin@mira.isdn.cs.tu-berlin.de>.
141da177e4SLinus Torvalds  */
151da177e4SLinus Torvalds 
161da177e4SLinus Torvalds #include <linux/time.h>
171da177e4SLinus Torvalds #include <linux/fs.h>
181da177e4SLinus Torvalds #include <linux/ufs_fs.h>
191da177e4SLinus Torvalds #include <linux/smp_lock.h>
201da177e4SLinus Torvalds #include <linux/buffer_head.h>
211da177e4SLinus Torvalds #include <linux/sched.h>
221da177e4SLinus Torvalds 
231da177e4SLinus Torvalds #include "swab.h"
241da177e4SLinus Torvalds #include "util.h"
251da177e4SLinus Torvalds 
261da177e4SLinus Torvalds #undef UFS_DIR_DEBUG
271da177e4SLinus Torvalds 
281da177e4SLinus Torvalds #ifdef UFS_DIR_DEBUG
291da177e4SLinus Torvalds #define UFSD(x) printk("(%s, %d), %s: ", __FILE__, __LINE__, __FUNCTION__); printk x;
301da177e4SLinus Torvalds #else
311da177e4SLinus Torvalds #define UFSD(x)
321da177e4SLinus Torvalds #endif
331da177e4SLinus Torvalds 
341da177e4SLinus Torvalds static int
351da177e4SLinus Torvalds ufs_check_dir_entry (const char *, struct inode *, struct ufs_dir_entry *,
361da177e4SLinus Torvalds 		     struct buffer_head *, unsigned long);
371da177e4SLinus Torvalds 
381da177e4SLinus Torvalds 
391da177e4SLinus Torvalds /*
401da177e4SLinus Torvalds  * NOTE! unlike strncmp, ufs_match returns 1 for success, 0 for failure.
411da177e4SLinus Torvalds  *
421da177e4SLinus Torvalds  * len <= UFS_MAXNAMLEN and de != NULL are guaranteed by caller.
431da177e4SLinus Torvalds  */
441da177e4SLinus Torvalds static inline int ufs_match(struct super_block *sb, int len,
451da177e4SLinus Torvalds 		const char * const name, struct ufs_dir_entry * de)
461da177e4SLinus Torvalds {
471da177e4SLinus Torvalds 	if (len != ufs_get_de_namlen(sb, de))
481da177e4SLinus Torvalds 		return 0;
491da177e4SLinus Torvalds 	if (!de->d_ino)
501da177e4SLinus Torvalds 		return 0;
511da177e4SLinus Torvalds 	return !memcmp(name, de->d_name, len);
521da177e4SLinus Torvalds }
531da177e4SLinus Torvalds 
541da177e4SLinus Torvalds /*
551da177e4SLinus Torvalds  * This is blatantly stolen from ext2fs
561da177e4SLinus Torvalds  */
571da177e4SLinus Torvalds static int
581da177e4SLinus Torvalds ufs_readdir (struct file * filp, void * dirent, filldir_t filldir)
591da177e4SLinus Torvalds {
601da177e4SLinus Torvalds 	struct inode *inode = filp->f_dentry->d_inode;
611da177e4SLinus Torvalds 	int error = 0;
621da177e4SLinus Torvalds 	unsigned long offset, lblk;
631da177e4SLinus Torvalds 	int i, stored;
641da177e4SLinus Torvalds 	struct buffer_head * bh;
651da177e4SLinus Torvalds 	struct ufs_dir_entry * de;
661da177e4SLinus Torvalds 	struct super_block * sb;
671da177e4SLinus Torvalds 	int de_reclen;
681da177e4SLinus Torvalds 	unsigned flags;
691da177e4SLinus Torvalds 	u64     blk= 0L;
701da177e4SLinus Torvalds 
711da177e4SLinus Torvalds 	lock_kernel();
721da177e4SLinus Torvalds 
731da177e4SLinus Torvalds 	sb = inode->i_sb;
741da177e4SLinus Torvalds 	flags = UFS_SB(sb)->s_flags;
751da177e4SLinus Torvalds 
761da177e4SLinus Torvalds 	UFSD(("ENTER, ino %lu  f_pos %lu\n", inode->i_ino, (unsigned long) filp->f_pos))
771da177e4SLinus Torvalds 
781da177e4SLinus Torvalds 	stored = 0;
791da177e4SLinus Torvalds 	bh = NULL;
801da177e4SLinus Torvalds 	offset = filp->f_pos & (sb->s_blocksize - 1);
811da177e4SLinus Torvalds 
821da177e4SLinus Torvalds 	while (!error && !stored && filp->f_pos < inode->i_size) {
831da177e4SLinus Torvalds 		lblk = (filp->f_pos) >> sb->s_blocksize_bits;
841da177e4SLinus Torvalds 		blk = ufs_frag_map(inode, lblk);
851da177e4SLinus Torvalds 		if (!blk || !(bh = sb_bread(sb, blk))) {
861da177e4SLinus Torvalds 			/* XXX - error - skip to the next block */
871da177e4SLinus Torvalds 			printk("ufs_readdir: "
881da177e4SLinus Torvalds 			       "dir inode %lu has a hole at offset %lu\n",
891da177e4SLinus Torvalds 			       inode->i_ino, (unsigned long int)filp->f_pos);
901da177e4SLinus Torvalds 			filp->f_pos += sb->s_blocksize - offset;
911da177e4SLinus Torvalds 			continue;
921da177e4SLinus Torvalds 		}
931da177e4SLinus Torvalds 
941da177e4SLinus Torvalds revalidate:
951da177e4SLinus Torvalds 		/* If the dir block has changed since the last call to
961da177e4SLinus Torvalds 		 * readdir(2), then we might be pointing to an invalid
971da177e4SLinus Torvalds 		 * dirent right now.  Scan from the start of the block
981da177e4SLinus Torvalds 		 * to make sure. */
991da177e4SLinus Torvalds 		if (filp->f_version != inode->i_version) {
1001da177e4SLinus Torvalds 			for (i = 0; i < sb->s_blocksize && i < offset; ) {
1011da177e4SLinus Torvalds 				de = (struct ufs_dir_entry *)(bh->b_data + i);
1021da177e4SLinus Torvalds 				/* It's too expensive to do a full
1031da177e4SLinus Torvalds 				 * dirent test each time round this
1041da177e4SLinus Torvalds 				 * loop, but we do have to test at
1051da177e4SLinus Torvalds 				 * least that it is non-zero.  A
1061da177e4SLinus Torvalds 				 * failure will be detected in the
1071da177e4SLinus Torvalds 				 * dirent test below. */
1081da177e4SLinus Torvalds 				de_reclen = fs16_to_cpu(sb, de->d_reclen);
1091da177e4SLinus Torvalds 				if (de_reclen < 1)
1101da177e4SLinus Torvalds 					break;
1111da177e4SLinus Torvalds 				i += de_reclen;
1121da177e4SLinus Torvalds 			}
1131da177e4SLinus Torvalds 			offset = i;
1141da177e4SLinus Torvalds 			filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1))
1151da177e4SLinus Torvalds 				| offset;
1161da177e4SLinus Torvalds 			filp->f_version = inode->i_version;
1171da177e4SLinus Torvalds 		}
1181da177e4SLinus Torvalds 
1191da177e4SLinus Torvalds 		while (!error && filp->f_pos < inode->i_size
1201da177e4SLinus Torvalds 		       && offset < sb->s_blocksize) {
1211da177e4SLinus Torvalds 			de = (struct ufs_dir_entry *) (bh->b_data + offset);
1221da177e4SLinus Torvalds 			/* XXX - put in a real ufs_check_dir_entry() */
1231da177e4SLinus Torvalds 			if ((de->d_reclen == 0) || (ufs_get_de_namlen(sb, de) == 0)) {
1241da177e4SLinus Torvalds 				filp->f_pos = (filp->f_pos &
1251da177e4SLinus Torvalds 				              (sb->s_blocksize - 1)) +
1261da177e4SLinus Torvalds 				               sb->s_blocksize;
1271da177e4SLinus Torvalds 				brelse(bh);
1281da177e4SLinus Torvalds 				unlock_kernel();
1291da177e4SLinus Torvalds 				return stored;
1301da177e4SLinus Torvalds 			}
1311da177e4SLinus Torvalds 			if (!ufs_check_dir_entry ("ufs_readdir", inode, de,
1321da177e4SLinus Torvalds 						   bh, offset)) {
1331da177e4SLinus Torvalds 				/* On error, skip the f_pos to the
1341da177e4SLinus Torvalds 				   next block. */
1351da177e4SLinus Torvalds 				filp->f_pos = (filp->f_pos |
1361da177e4SLinus Torvalds 				              (sb->s_blocksize - 1)) +
1371da177e4SLinus Torvalds 					       1;
1381da177e4SLinus Torvalds 				brelse (bh);
1391da177e4SLinus Torvalds 				unlock_kernel();
1401da177e4SLinus Torvalds 				return stored;
1411da177e4SLinus Torvalds 			}
1421da177e4SLinus Torvalds 			offset += fs16_to_cpu(sb, de->d_reclen);
1431da177e4SLinus Torvalds 			if (de->d_ino) {
1441da177e4SLinus Torvalds 				/* We might block in the next section
1451da177e4SLinus Torvalds 				 * if the data destination is
1461da177e4SLinus Torvalds 				 * currently swapped out.  So, use a
1471da177e4SLinus Torvalds 				 * version stamp to detect whether or
1481da177e4SLinus Torvalds 				 * not the directory has been modified
1491da177e4SLinus Torvalds 				 * during the copy operation. */
1501da177e4SLinus Torvalds 				unsigned long version = filp->f_version;
1511da177e4SLinus Torvalds 				unsigned char d_type = DT_UNKNOWN;
1521da177e4SLinus Torvalds 
1531da177e4SLinus Torvalds 				UFSD(("filldir(%s,%u)\n", de->d_name,
1541da177e4SLinus Torvalds 							fs32_to_cpu(sb, de->d_ino)))
1551da177e4SLinus Torvalds 				UFSD(("namlen %u\n", ufs_get_de_namlen(sb, de)))
1561da177e4SLinus Torvalds 
1571da177e4SLinus Torvalds 				if ((flags & UFS_DE_MASK) == UFS_DE_44BSD)
1581da177e4SLinus Torvalds 					d_type = de->d_u.d_44.d_type;
1591da177e4SLinus Torvalds 				error = filldir(dirent, de->d_name,
1601da177e4SLinus Torvalds 						ufs_get_de_namlen(sb, de), filp->f_pos,
1611da177e4SLinus Torvalds 						fs32_to_cpu(sb, de->d_ino), d_type);
1621da177e4SLinus Torvalds 				if (error)
1631da177e4SLinus Torvalds 					break;
1641da177e4SLinus Torvalds 				if (version != filp->f_version)
1651da177e4SLinus Torvalds 					goto revalidate;
1661da177e4SLinus Torvalds 				stored ++;
1671da177e4SLinus Torvalds 			}
1681da177e4SLinus Torvalds 			filp->f_pos += fs16_to_cpu(sb, de->d_reclen);
1691da177e4SLinus Torvalds 		}
1701da177e4SLinus Torvalds 		offset = 0;
1711da177e4SLinus Torvalds 		brelse (bh);
1721da177e4SLinus Torvalds 	}
1731da177e4SLinus Torvalds 	unlock_kernel();
1741da177e4SLinus Torvalds 	return 0;
1751da177e4SLinus Torvalds }
1761da177e4SLinus Torvalds 
1771da177e4SLinus Torvalds /*
1781da177e4SLinus Torvalds  * define how far ahead to read directories while searching them.
1791da177e4SLinus Torvalds  */
1801da177e4SLinus Torvalds #define NAMEI_RA_CHUNKS  2
1811da177e4SLinus Torvalds #define NAMEI_RA_BLOCKS  4
1821da177e4SLinus Torvalds #define NAMEI_RA_SIZE        (NAMEI_RA_CHUNKS * NAMEI_RA_BLOCKS)
1831da177e4SLinus Torvalds #define NAMEI_RA_INDEX(c,b)  (((c) * NAMEI_RA_BLOCKS) + (b))
1841da177e4SLinus Torvalds 
1851da177e4SLinus Torvalds /*
1861da177e4SLinus Torvalds  *	ufs_find_entry()
1871da177e4SLinus Torvalds  *
1881da177e4SLinus Torvalds  * finds an entry in the specified directory with the wanted name. It
1891da177e4SLinus Torvalds  * returns the cache buffer in which the entry was found, and the entry
1901da177e4SLinus Torvalds  * itself (as a parameter - res_bh). It does NOT read the inode of the
1911da177e4SLinus Torvalds  * entry - you'll have to do that yourself if you want to.
1921da177e4SLinus Torvalds  */
1931da177e4SLinus Torvalds struct ufs_dir_entry * ufs_find_entry (struct dentry *dentry,
1941da177e4SLinus Torvalds 	struct buffer_head ** res_bh)
1951da177e4SLinus Torvalds {
1961da177e4SLinus Torvalds 	struct super_block * sb;
1971da177e4SLinus Torvalds 	struct buffer_head * bh_use[NAMEI_RA_SIZE];
1981da177e4SLinus Torvalds 	struct buffer_head * bh_read[NAMEI_RA_SIZE];
1991da177e4SLinus Torvalds 	unsigned long offset;
2001da177e4SLinus Torvalds 	int block, toread, i, err;
2011da177e4SLinus Torvalds 	struct inode *dir = dentry->d_parent->d_inode;
2021da177e4SLinus Torvalds 	const char *name = dentry->d_name.name;
2031da177e4SLinus Torvalds 	int namelen = dentry->d_name.len;
2041da177e4SLinus Torvalds 
2051da177e4SLinus Torvalds 	UFSD(("ENTER, dir_ino %lu, name %s, namlen %u\n", dir->i_ino, name, namelen))
2061da177e4SLinus Torvalds 
2071da177e4SLinus Torvalds 	*res_bh = NULL;
2081da177e4SLinus Torvalds 
2091da177e4SLinus Torvalds 	sb = dir->i_sb;
2101da177e4SLinus Torvalds 
2111da177e4SLinus Torvalds 	if (namelen > UFS_MAXNAMLEN)
2121da177e4SLinus Torvalds 		return NULL;
2131da177e4SLinus Torvalds 
2141da177e4SLinus Torvalds 	memset (bh_use, 0, sizeof (bh_use));
2151da177e4SLinus Torvalds 	toread = 0;
2161da177e4SLinus Torvalds 	for (block = 0; block < NAMEI_RA_SIZE; ++block) {
2171da177e4SLinus Torvalds 		struct buffer_head * bh;
2181da177e4SLinus Torvalds 
2191da177e4SLinus Torvalds 		if ((block << sb->s_blocksize_bits) >= dir->i_size)
2201da177e4SLinus Torvalds 			break;
2211da177e4SLinus Torvalds 		bh = ufs_getfrag (dir, block, 0, &err);
2221da177e4SLinus Torvalds 		bh_use[block] = bh;
2231da177e4SLinus Torvalds 		if (bh && !buffer_uptodate(bh))
2241da177e4SLinus Torvalds 			bh_read[toread++] = bh;
2251da177e4SLinus Torvalds 	}
2261da177e4SLinus Torvalds 
2271da177e4SLinus Torvalds 	for (block = 0, offset = 0; offset < dir->i_size; block++) {
2281da177e4SLinus Torvalds 		struct buffer_head * bh;
2291da177e4SLinus Torvalds 		struct ufs_dir_entry * de;
2301da177e4SLinus Torvalds 		char * dlimit;
2311da177e4SLinus Torvalds 
2321da177e4SLinus Torvalds 		if ((block % NAMEI_RA_BLOCKS) == 0 && toread) {
2331da177e4SLinus Torvalds 			ll_rw_block (READ, toread, bh_read);
2341da177e4SLinus Torvalds 			toread = 0;
2351da177e4SLinus Torvalds 		}
2361da177e4SLinus Torvalds 		bh = bh_use[block % NAMEI_RA_SIZE];
2371da177e4SLinus Torvalds 		if (!bh) {
2381da177e4SLinus Torvalds 			ufs_error (sb, "ufs_find_entry",
2391da177e4SLinus Torvalds 				"directory #%lu contains a hole at offset %lu",
2401da177e4SLinus Torvalds 				dir->i_ino, offset);
2411da177e4SLinus Torvalds 			offset += sb->s_blocksize;
2421da177e4SLinus Torvalds 			continue;
2431da177e4SLinus Torvalds 		}
2441da177e4SLinus Torvalds 		wait_on_buffer (bh);
2451da177e4SLinus Torvalds 		if (!buffer_uptodate(bh)) {
2461da177e4SLinus Torvalds 			/*
2471da177e4SLinus Torvalds 			 * read error: all bets are off
2481da177e4SLinus Torvalds 			 */
2491da177e4SLinus Torvalds 			break;
2501da177e4SLinus Torvalds 		}
2511da177e4SLinus Torvalds 
2521da177e4SLinus Torvalds 		de = (struct ufs_dir_entry *) bh->b_data;
2531da177e4SLinus Torvalds 		dlimit = bh->b_data + sb->s_blocksize;
2541da177e4SLinus Torvalds 		while ((char *) de < dlimit && offset < dir->i_size) {
2551da177e4SLinus Torvalds 			/* this code is executed quadratically often */
2561da177e4SLinus Torvalds 			/* do minimal checking by hand */
2571da177e4SLinus Torvalds 			int de_len;
2581da177e4SLinus Torvalds 
2591da177e4SLinus Torvalds 			if ((char *) de + namelen <= dlimit &&
2601da177e4SLinus Torvalds 			    ufs_match(sb, namelen, name, de)) {
2611da177e4SLinus Torvalds 				/* found a match -
2621da177e4SLinus Torvalds 				just to be sure, do a full check */
2631da177e4SLinus Torvalds 				if (!ufs_check_dir_entry("ufs_find_entry",
2641da177e4SLinus Torvalds 				    dir, de, bh, offset))
2651da177e4SLinus Torvalds 					goto failed;
2661da177e4SLinus Torvalds 				for (i = 0; i < NAMEI_RA_SIZE; ++i) {
2671da177e4SLinus Torvalds 					if (bh_use[i] != bh)
2681da177e4SLinus Torvalds 						brelse (bh_use[i]);
2691da177e4SLinus Torvalds 				}
2701da177e4SLinus Torvalds 				*res_bh = bh;
2711da177e4SLinus Torvalds 				return de;
2721da177e4SLinus Torvalds 			}
2731da177e4SLinus Torvalds                         /* prevent looping on a bad block */
2741da177e4SLinus Torvalds 			de_len = fs16_to_cpu(sb, de->d_reclen);
2751da177e4SLinus Torvalds 			if (de_len <= 0)
2761da177e4SLinus Torvalds 				goto failed;
2771da177e4SLinus Torvalds 			offset += de_len;
2781da177e4SLinus Torvalds 			de = (struct ufs_dir_entry *) ((char *) de + de_len);
2791da177e4SLinus Torvalds 		}
2801da177e4SLinus Torvalds 
2811da177e4SLinus Torvalds 		brelse (bh);
2821da177e4SLinus Torvalds 		if (((block + NAMEI_RA_SIZE) << sb->s_blocksize_bits ) >=
2831da177e4SLinus Torvalds 		    dir->i_size)
2841da177e4SLinus Torvalds 			bh = NULL;
2851da177e4SLinus Torvalds 		else
2861da177e4SLinus Torvalds 			bh = ufs_getfrag (dir, block + NAMEI_RA_SIZE, 0, &err);
2871da177e4SLinus Torvalds 		bh_use[block % NAMEI_RA_SIZE] = bh;
2881da177e4SLinus Torvalds 		if (bh && !buffer_uptodate(bh))
2891da177e4SLinus Torvalds 			bh_read[toread++] = bh;
2901da177e4SLinus Torvalds 	}
2911da177e4SLinus Torvalds 
2921da177e4SLinus Torvalds failed:
2931da177e4SLinus Torvalds 	for (i = 0; i < NAMEI_RA_SIZE; ++i) brelse (bh_use[i]);
2941da177e4SLinus Torvalds 	UFSD(("EXIT\n"))
2951da177e4SLinus Torvalds 	return NULL;
2961da177e4SLinus Torvalds }
2971da177e4SLinus Torvalds 
2981da177e4SLinus Torvalds static int
2991da177e4SLinus Torvalds ufs_check_dir_entry (const char *function, struct inode *dir,
3001da177e4SLinus Torvalds 		     struct ufs_dir_entry *de, struct buffer_head *bh,
3011da177e4SLinus Torvalds 		     unsigned long offset)
3021da177e4SLinus Torvalds {
3031da177e4SLinus Torvalds 	struct super_block *sb = dir->i_sb;
3041da177e4SLinus Torvalds 	const char *error_msg = NULL;
3051da177e4SLinus Torvalds 	int rlen = fs16_to_cpu(sb, de->d_reclen);
3061da177e4SLinus Torvalds 
3071da177e4SLinus Torvalds 	if (rlen < UFS_DIR_REC_LEN(1))
3081da177e4SLinus Torvalds 		error_msg = "reclen is smaller than minimal";
3091da177e4SLinus Torvalds 	else if (rlen % 4 != 0)
3101da177e4SLinus Torvalds 		error_msg = "reclen % 4 != 0";
3111da177e4SLinus Torvalds 	else if (rlen < UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, de)))
3121da177e4SLinus Torvalds 		error_msg = "reclen is too small for namlen";
3131da177e4SLinus Torvalds 	else if (((char *) de - bh->b_data) + rlen > dir->i_sb->s_blocksize)
3141da177e4SLinus Torvalds 		error_msg = "directory entry across blocks";
3151da177e4SLinus Torvalds 	else if (fs32_to_cpu(sb, de->d_ino) > (UFS_SB(sb)->s_uspi->s_ipg *
3161da177e4SLinus Torvalds 				      UFS_SB(sb)->s_uspi->s_ncg))
3171da177e4SLinus Torvalds 		error_msg = "inode out of bounds";
3181da177e4SLinus Torvalds 
3191da177e4SLinus Torvalds 	if (error_msg != NULL)
3201da177e4SLinus Torvalds 		ufs_error (sb, function, "bad entry in directory #%lu, size %Lu: %s - "
3211da177e4SLinus Torvalds 			    "offset=%lu, inode=%lu, reclen=%d, namlen=%d",
3221da177e4SLinus Torvalds 			    dir->i_ino, dir->i_size, error_msg, offset,
3231da177e4SLinus Torvalds 			    (unsigned long)fs32_to_cpu(sb, de->d_ino),
3241da177e4SLinus Torvalds 			    rlen, ufs_get_de_namlen(sb, de));
3251da177e4SLinus Torvalds 
3261da177e4SLinus Torvalds 	return (error_msg == NULL ? 1 : 0);
3271da177e4SLinus Torvalds }
3281da177e4SLinus Torvalds 
3291da177e4SLinus Torvalds struct ufs_dir_entry *ufs_dotdot(struct inode *dir, struct buffer_head **p)
3301da177e4SLinus Torvalds {
3311da177e4SLinus Torvalds 	int err;
3321da177e4SLinus Torvalds 	struct buffer_head *bh = ufs_bread (dir, 0, 0, &err);
3331da177e4SLinus Torvalds 	struct ufs_dir_entry *res = NULL;
3341da177e4SLinus Torvalds 
3351da177e4SLinus Torvalds 	if (bh) {
3361da177e4SLinus Torvalds 		res = (struct ufs_dir_entry *) bh->b_data;
3371da177e4SLinus Torvalds 		res = (struct ufs_dir_entry *)((char *)res +
3381da177e4SLinus Torvalds 			fs16_to_cpu(dir->i_sb, res->d_reclen));
3391da177e4SLinus Torvalds 	}
3401da177e4SLinus Torvalds 	*p = bh;
3411da177e4SLinus Torvalds 	return res;
3421da177e4SLinus Torvalds }
3431da177e4SLinus Torvalds ino_t ufs_inode_by_name(struct inode * dir, struct dentry *dentry)
3441da177e4SLinus Torvalds {
3451da177e4SLinus Torvalds 	ino_t res = 0;
3461da177e4SLinus Torvalds 	struct ufs_dir_entry * de;
3471da177e4SLinus Torvalds 	struct buffer_head *bh;
3481da177e4SLinus Torvalds 
3491da177e4SLinus Torvalds 	de = ufs_find_entry (dentry, &bh);
3501da177e4SLinus Torvalds 	if (de) {
3511da177e4SLinus Torvalds 		res = fs32_to_cpu(dir->i_sb, de->d_ino);
3521da177e4SLinus Torvalds 		brelse(bh);
3531da177e4SLinus Torvalds 	}
3541da177e4SLinus Torvalds 	return res;
3551da177e4SLinus Torvalds }
3561da177e4SLinus Torvalds 
3571da177e4SLinus Torvalds void ufs_set_link(struct inode *dir, struct ufs_dir_entry *de,
3581da177e4SLinus Torvalds 		struct buffer_head *bh, struct inode *inode)
3591da177e4SLinus Torvalds {
3601da177e4SLinus Torvalds 	dir->i_version++;
3611da177e4SLinus Torvalds 	de->d_ino = cpu_to_fs32(dir->i_sb, inode->i_ino);
3621da177e4SLinus Torvalds 	mark_buffer_dirty(bh);
3631da177e4SLinus Torvalds 	if (IS_DIRSYNC(dir))
3641da177e4SLinus Torvalds 		sync_dirty_buffer(bh);
3651da177e4SLinus Torvalds 	brelse (bh);
3661da177e4SLinus Torvalds }
3671da177e4SLinus Torvalds 
3681da177e4SLinus Torvalds /*
3691da177e4SLinus Torvalds  *	ufs_add_entry()
3701da177e4SLinus Torvalds  *
3711da177e4SLinus Torvalds  * adds a file entry to the specified directory, using the same
3721da177e4SLinus Torvalds  * semantics as ufs_find_entry(). It returns NULL if it failed.
3731da177e4SLinus Torvalds  */
3741da177e4SLinus Torvalds int ufs_add_link(struct dentry *dentry, struct inode *inode)
3751da177e4SLinus Torvalds {
3761da177e4SLinus Torvalds 	struct super_block * sb;
3771da177e4SLinus Torvalds 	struct ufs_sb_private_info * uspi;
3781da177e4SLinus Torvalds 	unsigned long offset;
3791da177e4SLinus Torvalds 	unsigned fragoff;
3801da177e4SLinus Torvalds 	unsigned short rec_len;
3811da177e4SLinus Torvalds 	struct buffer_head * bh;
3821da177e4SLinus Torvalds 	struct ufs_dir_entry * de, * de1;
3831da177e4SLinus Torvalds 	struct inode *dir = dentry->d_parent->d_inode;
3841da177e4SLinus Torvalds 	const char *name = dentry->d_name.name;
3851da177e4SLinus Torvalds 	int namelen = dentry->d_name.len;
3861da177e4SLinus Torvalds 	int err;
3871da177e4SLinus Torvalds 
3881da177e4SLinus Torvalds 	UFSD(("ENTER, name %s, namelen %u\n", name, namelen))
3891da177e4SLinus Torvalds 
3901da177e4SLinus Torvalds 	sb = dir->i_sb;
3911da177e4SLinus Torvalds 	uspi = UFS_SB(sb)->s_uspi;
3921da177e4SLinus Torvalds 
3931da177e4SLinus Torvalds 	if (!namelen)
3941da177e4SLinus Torvalds 		return -EINVAL;
3951da177e4SLinus Torvalds 	bh = ufs_bread (dir, 0, 0, &err);
3961da177e4SLinus Torvalds 	if (!bh)
3971da177e4SLinus Torvalds 		return err;
3981da177e4SLinus Torvalds 	rec_len = UFS_DIR_REC_LEN(namelen);
3991da177e4SLinus Torvalds 	offset = 0;
4001da177e4SLinus Torvalds 	de = (struct ufs_dir_entry *) bh->b_data;
4011da177e4SLinus Torvalds 	while (1) {
4021da177e4SLinus Torvalds 		if ((char *)de >= UFS_SECTOR_SIZE + bh->b_data) {
4031da177e4SLinus Torvalds 			fragoff = offset & ~uspi->s_fmask;
4041da177e4SLinus Torvalds 			if (fragoff != 0 && fragoff != UFS_SECTOR_SIZE)
4051da177e4SLinus Torvalds 				ufs_error (sb, "ufs_add_entry", "internal error"
4061da177e4SLinus Torvalds 					" fragoff %u", fragoff);
4071da177e4SLinus Torvalds 			if (!fragoff) {
4081da177e4SLinus Torvalds 				brelse (bh);
4091da177e4SLinus Torvalds 				bh = ufs_bread (dir, offset >> sb->s_blocksize_bits, 1, &err);
4101da177e4SLinus Torvalds 				if (!bh)
4111da177e4SLinus Torvalds 					return err;
4121da177e4SLinus Torvalds 			}
4131da177e4SLinus Torvalds 			if (dir->i_size <= offset) {
4141da177e4SLinus Torvalds 				if (dir->i_size == 0) {
4151da177e4SLinus Torvalds 					brelse(bh);
4161da177e4SLinus Torvalds 					return -ENOENT;
4171da177e4SLinus Torvalds 				}
4181da177e4SLinus Torvalds 				de = (struct ufs_dir_entry *) (bh->b_data + fragoff);
4191da177e4SLinus Torvalds 				de->d_ino = 0;
4201da177e4SLinus Torvalds 				de->d_reclen = cpu_to_fs16(sb, UFS_SECTOR_SIZE);
4211da177e4SLinus Torvalds 				ufs_set_de_namlen(sb, de, 0);
4221da177e4SLinus Torvalds 				dir->i_size = offset + UFS_SECTOR_SIZE;
4231da177e4SLinus Torvalds 				mark_inode_dirty(dir);
4241da177e4SLinus Torvalds 			} else {
4251da177e4SLinus Torvalds 				de = (struct ufs_dir_entry *) bh->b_data;
4261da177e4SLinus Torvalds 			}
4271da177e4SLinus Torvalds 		}
4281da177e4SLinus Torvalds 		if (!ufs_check_dir_entry ("ufs_add_entry", dir, de, bh, offset)) {
4291da177e4SLinus Torvalds 			brelse (bh);
4301da177e4SLinus Torvalds 			return -ENOENT;
4311da177e4SLinus Torvalds 		}
4321da177e4SLinus Torvalds 		if (ufs_match(sb, namelen, name, de)) {
4331da177e4SLinus Torvalds 			brelse (bh);
4341da177e4SLinus Torvalds 			return -EEXIST;
4351da177e4SLinus Torvalds 		}
4361da177e4SLinus Torvalds 		if (de->d_ino == 0 && fs16_to_cpu(sb, de->d_reclen) >= rec_len)
4371da177e4SLinus Torvalds 			break;
4381da177e4SLinus Torvalds 
4391da177e4SLinus Torvalds 		if (fs16_to_cpu(sb, de->d_reclen) >=
4401da177e4SLinus Torvalds 		     UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, de)) + rec_len)
4411da177e4SLinus Torvalds 			break;
4421da177e4SLinus Torvalds 		offset += fs16_to_cpu(sb, de->d_reclen);
4431da177e4SLinus Torvalds 		de = (struct ufs_dir_entry *) ((char *) de + fs16_to_cpu(sb, de->d_reclen));
4441da177e4SLinus Torvalds 	}
4451da177e4SLinus Torvalds 
4461da177e4SLinus Torvalds 	if (de->d_ino) {
4471da177e4SLinus Torvalds 		de1 = (struct ufs_dir_entry *) ((char *) de +
4481da177e4SLinus Torvalds 			UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, de)));
4491da177e4SLinus Torvalds 		de1->d_reclen =
4501da177e4SLinus Torvalds 			cpu_to_fs16(sb, fs16_to_cpu(sb, de->d_reclen) -
4511da177e4SLinus Torvalds 				UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, de)));
4521da177e4SLinus Torvalds 		de->d_reclen =
4531da177e4SLinus Torvalds 			cpu_to_fs16(sb, UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, de)));
4541da177e4SLinus Torvalds 		de = de1;
4551da177e4SLinus Torvalds 	}
4561da177e4SLinus Torvalds 	de->d_ino = 0;
4571da177e4SLinus Torvalds 	ufs_set_de_namlen(sb, de, namelen);
4581da177e4SLinus Torvalds 	memcpy (de->d_name, name, namelen + 1);
4591da177e4SLinus Torvalds 	de->d_ino = cpu_to_fs32(sb, inode->i_ino);
4601da177e4SLinus Torvalds 	ufs_set_de_type(sb, de, inode->i_mode);
4611da177e4SLinus Torvalds 	mark_buffer_dirty(bh);
4621da177e4SLinus Torvalds 	if (IS_DIRSYNC(dir))
4631da177e4SLinus Torvalds 		sync_dirty_buffer(bh);
4641da177e4SLinus Torvalds 	brelse (bh);
4651da177e4SLinus Torvalds 	dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
4661da177e4SLinus Torvalds 	dir->i_version++;
4671da177e4SLinus Torvalds 	mark_inode_dirty(dir);
4681da177e4SLinus Torvalds 
4691da177e4SLinus Torvalds 	UFSD(("EXIT\n"))
4701da177e4SLinus Torvalds 	return 0;
4711da177e4SLinus Torvalds }
4721da177e4SLinus Torvalds 
4731da177e4SLinus Torvalds /*
4741da177e4SLinus Torvalds  * ufs_delete_entry deletes a directory entry by merging it with the
4751da177e4SLinus Torvalds  * previous entry.
4761da177e4SLinus Torvalds  */
4771da177e4SLinus Torvalds int ufs_delete_entry (struct inode * inode, struct ufs_dir_entry * dir,
4781da177e4SLinus Torvalds 	struct buffer_head * bh )
4791da177e4SLinus Torvalds 
4801da177e4SLinus Torvalds {
4811da177e4SLinus Torvalds 	struct super_block * sb;
4821da177e4SLinus Torvalds 	struct ufs_dir_entry * de, * pde;
4831da177e4SLinus Torvalds 	unsigned i;
4841da177e4SLinus Torvalds 
4851da177e4SLinus Torvalds 	UFSD(("ENTER\n"))
4861da177e4SLinus Torvalds 
4871da177e4SLinus Torvalds 	sb = inode->i_sb;
4881da177e4SLinus Torvalds 	i = 0;
4891da177e4SLinus Torvalds 	pde = NULL;
4901da177e4SLinus Torvalds 	de = (struct ufs_dir_entry *) bh->b_data;
4911da177e4SLinus Torvalds 
4921da177e4SLinus Torvalds 	UFSD(("ino %u, reclen %u, namlen %u, name %s\n",
4931da177e4SLinus Torvalds 		fs32_to_cpu(sb, de->d_ino),
494221fc10eSEvgeniy 		fs16_to_cpu(sb, de->d_reclen),
4951da177e4SLinus Torvalds 		ufs_get_de_namlen(sb, de), de->d_name))
4961da177e4SLinus Torvalds 
4971da177e4SLinus Torvalds 	while (i < bh->b_size) {
4981da177e4SLinus Torvalds 		if (!ufs_check_dir_entry ("ufs_delete_entry", inode, de, bh, i)) {
4991da177e4SLinus Torvalds 			brelse(bh);
5001da177e4SLinus Torvalds 			return -EIO;
5011da177e4SLinus Torvalds 		}
5021da177e4SLinus Torvalds 		if (de == dir)  {
5031da177e4SLinus Torvalds 			if (pde)
5041da177e4SLinus Torvalds 				fs16_add(sb, &pde->d_reclen,
5051da177e4SLinus Torvalds 					fs16_to_cpu(sb, dir->d_reclen));
5061da177e4SLinus Torvalds 			dir->d_ino = 0;
5071da177e4SLinus Torvalds 			inode->i_version++;
5081da177e4SLinus Torvalds 			inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC;
5091da177e4SLinus Torvalds 			mark_inode_dirty(inode);
5101da177e4SLinus Torvalds 			mark_buffer_dirty(bh);
5111da177e4SLinus Torvalds 			if (IS_DIRSYNC(inode))
5121da177e4SLinus Torvalds 				sync_dirty_buffer(bh);
5131da177e4SLinus Torvalds 			brelse(bh);
5141da177e4SLinus Torvalds 			UFSD(("EXIT\n"))
5151da177e4SLinus Torvalds 			return 0;
5161da177e4SLinus Torvalds 		}
5171da177e4SLinus Torvalds 		i += fs16_to_cpu(sb, de->d_reclen);
5181da177e4SLinus Torvalds 		if (i == UFS_SECTOR_SIZE) pde = NULL;
5191da177e4SLinus Torvalds 		else pde = de;
5201da177e4SLinus Torvalds 		de = (struct ufs_dir_entry *)
5211da177e4SLinus Torvalds 		    ((char *) de + fs16_to_cpu(sb, de->d_reclen));
5221da177e4SLinus Torvalds 		if (i == UFS_SECTOR_SIZE && de->d_reclen == 0)
5231da177e4SLinus Torvalds 			break;
5241da177e4SLinus Torvalds 	}
5251da177e4SLinus Torvalds 	UFSD(("EXIT\n"))
5261da177e4SLinus Torvalds 	brelse(bh);
5271da177e4SLinus Torvalds 	return -ENOENT;
5281da177e4SLinus Torvalds }
5291da177e4SLinus Torvalds 
5301da177e4SLinus Torvalds int ufs_make_empty(struct inode * inode, struct inode *dir)
5311da177e4SLinus Torvalds {
5321da177e4SLinus Torvalds 	struct super_block * sb = dir->i_sb;
5331da177e4SLinus Torvalds 	struct buffer_head * dir_block;
5341da177e4SLinus Torvalds 	struct ufs_dir_entry * de;
5351da177e4SLinus Torvalds 	int err;
5361da177e4SLinus Torvalds 
5371da177e4SLinus Torvalds 	dir_block = ufs_bread (inode, 0, 1, &err);
5381da177e4SLinus Torvalds 	if (!dir_block)
5391da177e4SLinus Torvalds 		return err;
5401da177e4SLinus Torvalds 
5411da177e4SLinus Torvalds 	inode->i_blocks = sb->s_blocksize / UFS_SECTOR_SIZE;
5421da177e4SLinus Torvalds 	de = (struct ufs_dir_entry *) dir_block->b_data;
5431da177e4SLinus Torvalds 	de->d_ino = cpu_to_fs32(sb, inode->i_ino);
5441da177e4SLinus Torvalds 	ufs_set_de_type(sb, de, inode->i_mode);
5451da177e4SLinus Torvalds 	ufs_set_de_namlen(sb, de, 1);
5461da177e4SLinus Torvalds 	de->d_reclen = cpu_to_fs16(sb, UFS_DIR_REC_LEN(1));
5471da177e4SLinus Torvalds 	strcpy (de->d_name, ".");
5481da177e4SLinus Torvalds 	de = (struct ufs_dir_entry *)
5491da177e4SLinus Torvalds 		((char *)de + fs16_to_cpu(sb, de->d_reclen));
5501da177e4SLinus Torvalds 	de->d_ino = cpu_to_fs32(sb, dir->i_ino);
5511da177e4SLinus Torvalds 	ufs_set_de_type(sb, de, dir->i_mode);
5521da177e4SLinus Torvalds 	de->d_reclen = cpu_to_fs16(sb, UFS_SECTOR_SIZE - UFS_DIR_REC_LEN(1));
5531da177e4SLinus Torvalds 	ufs_set_de_namlen(sb, de, 2);
5541da177e4SLinus Torvalds 	strcpy (de->d_name, "..");
5551da177e4SLinus Torvalds 	mark_buffer_dirty(dir_block);
5561da177e4SLinus Torvalds 	brelse (dir_block);
5571da177e4SLinus Torvalds 	mark_inode_dirty(inode);
5581da177e4SLinus Torvalds 	return 0;
5591da177e4SLinus Torvalds }
5601da177e4SLinus Torvalds 
5611da177e4SLinus Torvalds /*
5621da177e4SLinus Torvalds  * routine to check that the specified directory is empty (for rmdir)
5631da177e4SLinus Torvalds  */
5641da177e4SLinus Torvalds int ufs_empty_dir (struct inode * inode)
5651da177e4SLinus Torvalds {
5661da177e4SLinus Torvalds 	struct super_block * sb;
5671da177e4SLinus Torvalds 	unsigned long offset;
5681da177e4SLinus Torvalds 	struct buffer_head * bh;
5691da177e4SLinus Torvalds 	struct ufs_dir_entry * de, * de1;
5701da177e4SLinus Torvalds 	int err;
5711da177e4SLinus Torvalds 
5721da177e4SLinus Torvalds 	sb = inode->i_sb;
5731da177e4SLinus Torvalds 
5741da177e4SLinus Torvalds 	if (inode->i_size < UFS_DIR_REC_LEN(1) + UFS_DIR_REC_LEN(2) ||
5751da177e4SLinus Torvalds 	    !(bh = ufs_bread (inode, 0, 0, &err))) {
5761da177e4SLinus Torvalds 	    	ufs_warning (inode->i_sb, "empty_dir",
5771da177e4SLinus Torvalds 			      "bad directory (dir #%lu) - no data block",
5781da177e4SLinus Torvalds 			      inode->i_ino);
5791da177e4SLinus Torvalds 		return 1;
5801da177e4SLinus Torvalds 	}
5811da177e4SLinus Torvalds 	de = (struct ufs_dir_entry *) bh->b_data;
5821da177e4SLinus Torvalds 	de1 = (struct ufs_dir_entry *)
5831da177e4SLinus Torvalds 		((char *)de + fs16_to_cpu(sb, de->d_reclen));
5841da177e4SLinus Torvalds 	if (fs32_to_cpu(sb, de->d_ino) != inode->i_ino || de1->d_ino == 0 ||
5851da177e4SLinus Torvalds 	     strcmp (".", de->d_name) || strcmp ("..", de1->d_name)) {
5861da177e4SLinus Torvalds 	    	ufs_warning (inode->i_sb, "empty_dir",
5871da177e4SLinus Torvalds 			      "bad directory (dir #%lu) - no `.' or `..'",
5881da177e4SLinus Torvalds 			      inode->i_ino);
5891da177e4SLinus Torvalds 		return 1;
5901da177e4SLinus Torvalds 	}
5911da177e4SLinus Torvalds 	offset = fs16_to_cpu(sb, de->d_reclen) + fs16_to_cpu(sb, de1->d_reclen);
5921da177e4SLinus Torvalds 	de = (struct ufs_dir_entry *)
5931da177e4SLinus Torvalds 		((char *)de1 + fs16_to_cpu(sb, de1->d_reclen));
5941da177e4SLinus Torvalds 	while (offset < inode->i_size ) {
5951da177e4SLinus Torvalds 		if (!bh || (void *) de >= (void *) (bh->b_data + sb->s_blocksize)) {
5961da177e4SLinus Torvalds 			brelse (bh);
5971da177e4SLinus Torvalds 			bh = ufs_bread (inode, offset >> sb->s_blocksize_bits, 1, &err);
5981da177e4SLinus Torvalds 	 		if (!bh) {
5991da177e4SLinus Torvalds 				ufs_error (sb, "empty_dir",
6001da177e4SLinus Torvalds 					    "directory #%lu contains a hole at offset %lu",
6011da177e4SLinus Torvalds 					    inode->i_ino, offset);
6021da177e4SLinus Torvalds 				offset += sb->s_blocksize;
6031da177e4SLinus Torvalds 				continue;
6041da177e4SLinus Torvalds 			}
6051da177e4SLinus Torvalds 			de = (struct ufs_dir_entry *) bh->b_data;
6061da177e4SLinus Torvalds 		}
6071da177e4SLinus Torvalds 		if (!ufs_check_dir_entry ("empty_dir", inode, de, bh, offset)) {
6081da177e4SLinus Torvalds 			brelse (bh);
6091da177e4SLinus Torvalds 			return 1;
6101da177e4SLinus Torvalds 		}
6111da177e4SLinus Torvalds 		if (de->d_ino) {
6121da177e4SLinus Torvalds 			brelse (bh);
6131da177e4SLinus Torvalds 			return 0;
6141da177e4SLinus Torvalds 		}
6151da177e4SLinus Torvalds 		offset += fs16_to_cpu(sb, de->d_reclen);
6161da177e4SLinus Torvalds 		de = (struct ufs_dir_entry *)
6171da177e4SLinus Torvalds 			((char *)de + fs16_to_cpu(sb, de->d_reclen));
6181da177e4SLinus Torvalds 	}
6191da177e4SLinus Torvalds 	brelse (bh);
6201da177e4SLinus Torvalds 	return 1;
6211da177e4SLinus Torvalds }
6221da177e4SLinus Torvalds 
623*4b6f5d20SArjan van de Ven const struct file_operations ufs_dir_operations = {
6241da177e4SLinus Torvalds 	.read		= generic_read_dir,
6251da177e4SLinus Torvalds 	.readdir	= ufs_readdir,
6261da177e4SLinus Torvalds 	.fsync		= file_fsync,
6271da177e4SLinus Torvalds };
628