dir.c 6.2 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
/*
 * dir.c
 *
 * PURPOSE
 *  Directory handling routines for the OSTA-UDF(tm) filesystem.
 *
 * COPYRIGHT
 *	This file is distributed under the terms of the GNU General Public
 *	License (GPL). Copies of the GPL can be obtained from:
 *		ftp://prep.ai.mit.edu/pub/gnu/GPL
 *	Each contributing author retains all rights to their own work.
 *
 *  (C) 1998-2004 Ben Fennema
 *
 * HISTORY
 *
 *  10/05/98 dgb  Split directory operations into its own file
 *                Implemented directory reads via do_udf_readdir
 *  10/06/98      Made directory operations work!
 *  11/17/98      Rewrote directory to support ICBTAG_FLAG_AD_LONG
 *  11/25/98 blf  Rewrote directory handling (readdir+lookup) to support reading
 *                across blocks.
 *  12/12/98      Split out the lookup code to namei.c. bulk of directory
 *                code now in directory.c:udf_fileident_read.
 */

#include "udfdecl.h"

#include <linux/string.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/buffer_head.h>

#include "udf_i.h"
#include "udf_sb.h"

/* Prototypes for file operations */
static int udf_readdir(struct file *, void *, filldir_t);
static int do_udf_readdir(struct inode *, struct file *, filldir_t, void *);

/* readdir and lookup functions */

45
const struct file_operations udf_dir_operations = {
46 47 48 49
	.read = generic_read_dir,
	.readdir = udf_readdir,
	.ioctl = udf_ioctl,
	.fsync = udf_fsync_file,
L
Linus Torvalds 已提交
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
};

/*
 * udf_readdir
 *
 * PURPOSE
 *	Read a directory entry.
 *
 * DESCRIPTION
 *	Optional - sys_getdents() will return -ENOTDIR if this routine is not
 *	available.
 *
 *	Refer to sys_getdents() in fs/readdir.c
 *	sys_getdents() -> .
 *
 * PRE-CONDITIONS
 *	filp			Pointer to directory file.
 *	buf			Pointer to directory entry buffer.
 *	filldir			Pointer to filldir function.
 *
 * POST-CONDITIONS
 *	<return>		>=0 on success.
 *
 * HISTORY
 *	July 1, 1997 - Andrew E. Mileski
 *	Written, tested, and released.
 */

int udf_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
J
Josef Sipek 已提交
80
	struct inode *dir = filp->f_path.dentry->d_inode;
L
Linus Torvalds 已提交
81 82 83 84
	int result;

	lock_kernel();

85 86 87
	if (filp->f_pos == 0) {
		if (filldir(dirent, ".", 1, filp->f_pos, dir->i_ino, DT_DIR) <
		    0) {
L
Linus Torvalds 已提交
88 89 90
			unlock_kernel();
			return 0;
		}
91
		filp->f_pos++;
L
Linus Torvalds 已提交
92 93 94 95
	}

	result = do_udf_readdir(dir, filp, filldir, dirent);
	unlock_kernel();
96
	return result;
L
Linus Torvalds 已提交
97 98
}

99 100 101
static int
do_udf_readdir(struct inode *dir, struct file *filp, filldir_t filldir,
	       void *dirent)
L
Linus Torvalds 已提交
102 103
{
	struct udf_fileident_bh fibh;
104
	struct fileIdentDesc *fi = NULL;
L
Linus Torvalds 已提交
105 106 107 108 109 110 111 112 113
	struct fileIdentDesc cfi;
	int block, iblock;
	loff_t nf_pos = filp->f_pos - 1;
	int flen;
	char fname[UDF_NAME_LEN];
	char *nameptr;
	uint16_t liu;
	uint8_t lfi;
	loff_t size = (udf_ext0_offset(dir) + dir->i_size) >> 2;
J
Jan Kara 已提交
114 115 116
	struct buffer_head *tmp, *bha[16];
	kernel_lb_addr eloc;
	uint32_t elen;
117
	sector_t offset;
L
Linus Torvalds 已提交
118 119
	int i, num;
	unsigned int dt_type;
120
	struct extent_position epos = { NULL, 0, {0, 0} };
L
Linus Torvalds 已提交
121 122 123 124 125 126 127

	if (nf_pos >= size)
		return 0;

	if (nf_pos == 0)
		nf_pos = (udf_ext0_offset(dir) >> 2);

128 129
	fibh.soffset = fibh.eoffset =
	    (nf_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2;
L
Linus Torvalds 已提交
130 131 132
	if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_IN_ICB)
		fibh.sbh = fibh.ebh = NULL;
	else if (inode_bmap(dir, nf_pos >> (dir->i_sb->s_blocksize_bits - 2),
133 134
			    &epos, &eloc, &elen,
			    &offset) == (EXT_RECORDED_ALLOCATED >> 30)) {
L
Linus Torvalds 已提交
135
		block = udf_get_lb_pblock(dir->i_sb, eloc, offset);
136
		if ((++offset << dir->i_sb->s_blocksize_bits) < elen) {
L
Linus Torvalds 已提交
137
			if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT)
J
Jan Kara 已提交
138
				epos.offset -= sizeof(short_ad);
L
Linus Torvalds 已提交
139
			else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG)
J
Jan Kara 已提交
140
				epos.offset -= sizeof(long_ad);
141
		} else
L
Linus Torvalds 已提交
142 143
			offset = 0;

144
		if (!(fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block))) {
J
Jan Kara 已提交
145
			brelse(epos.bh);
L
Linus Torvalds 已提交
146 147
			return -EIO;
		}
148 149

		if (!(offset & ((16 >> (dir->i_sb->s_blocksize_bits - 9)) - 1))) {
L
Linus Torvalds 已提交
150
			i = 16 >> (dir->i_sb->s_blocksize_bits - 9);
151 152 153 154 155 156 157
			if (i + offset > (elen >> dir->i_sb->s_blocksize_bits))
				i = (elen >> dir->i_sb->s_blocksize_bits) -
				    offset;
			for (num = 0; i > 0; i--) {
				block =
				    udf_get_lb_pblock(dir->i_sb, eloc,
						      offset + i);
L
Linus Torvalds 已提交
158
				tmp = udf_tgetblk(dir->i_sb, block);
159 160
				if (tmp && !buffer_uptodate(tmp)
				    && !buffer_locked(tmp))
L
Linus Torvalds 已提交
161 162 163 164
					bha[num++] = tmp;
				else
					brelse(tmp);
			}
165
			if (num) {
L
Linus Torvalds 已提交
166
				ll_rw_block(READA, num, bha);
167
				for (i = 0; i < num; i++)
L
Linus Torvalds 已提交
168 169 170
					brelse(bha[i]);
			}
		}
171
	} else {
J
Jan Kara 已提交
172
		brelse(epos.bh);
L
Linus Torvalds 已提交
173 174 175
		return -ENOENT;
	}

176
	while (nf_pos < size) {
L
Linus Torvalds 已提交
177 178
		filp->f_pos = nf_pos + 1;

179 180
		fi = udf_fileident_read(dir, &nf_pos, &fibh, &cfi, &epos, &eloc,
					&elen, &offset);
L
Linus Torvalds 已提交
181

182
		if (!fi) {
L
Linus Torvalds 已提交
183
			if (fibh.sbh != fibh.ebh)
J
Jan Kara 已提交
184 185 186
				brelse(fibh.ebh);
			brelse(fibh.sbh);
			brelse(epos.bh);
L
Linus Torvalds 已提交
187 188 189 190 191 192 193 194
			return 0;
		}

		liu = le16_to_cpu(cfi.lengthOfImpUse);
		lfi = cfi.lengthFileIdent;

		if (fibh.sbh == fibh.ebh)
			nameptr = fi->fileIdent + liu;
195
		else {
L
Linus Torvalds 已提交
196 197
			int poffset;	/* Unpaded ending offset */

198 199 200
			poffset =
			    fibh.soffset + sizeof(struct fileIdentDesc) + liu +
			    lfi;
L
Linus Torvalds 已提交
201 202

			if (poffset >= lfi)
203 204 205
				nameptr =
				    (char *)(fibh.ebh->b_data + poffset - lfi);
			else {
L
Linus Torvalds 已提交
206
				nameptr = fname;
207 208 209 210
				memcpy(nameptr, fi->fileIdent + liu,
				       lfi - poffset);
				memcpy(nameptr + lfi - poffset,
				       fibh.ebh->b_data, poffset);
L
Linus Torvalds 已提交
211 212 213
			}
		}

214 215
		if ((cfi.fileCharacteristics & FID_FILE_CHAR_DELETED) != 0) {
			if (!UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNDELETE))
L
Linus Torvalds 已提交
216 217
				continue;
		}
218 219 220

		if ((cfi.fileCharacteristics & FID_FILE_CHAR_HIDDEN) != 0) {
			if (!UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNHIDE))
L
Linus Torvalds 已提交
221 222 223
				continue;
		}

224
		if (cfi.fileCharacteristics & FID_FILE_CHAR_PARENT) {
J
Josef Sipek 已提交
225
			iblock = parent_ino(filp->f_path.dentry);
L
Linus Torvalds 已提交
226 227 228
			flen = 2;
			memcpy(fname, "..", flen);
			dt_type = DT_DIR;
229
		} else {
L
Linus Torvalds 已提交
230 231 232 233 234 235 236
			kernel_lb_addr tloc = lelb_to_cpu(cfi.icb.extLocation);

			iblock = udf_get_lb_pblock(dir->i_sb, tloc, 0);
			flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi);
			dt_type = DT_UNKNOWN;
		}

237 238 239 240
		if (flen) {
			if (filldir
			    (dirent, fname, flen, filp->f_pos, iblock,
			     dt_type) < 0) {
L
Linus Torvalds 已提交
241
				if (fibh.sbh != fibh.ebh)
J
Jan Kara 已提交
242 243 244
					brelse(fibh.ebh);
				brelse(fibh.sbh);
				brelse(epos.bh);
245
				return 0;
L
Linus Torvalds 已提交
246 247
			}
		}
248
	}			/* end while */
L
Linus Torvalds 已提交
249 250 251 252

	filp->f_pos = nf_pos + 1;

	if (fibh.sbh != fibh.ebh)
J
Jan Kara 已提交
253 254 255
		brelse(fibh.ebh);
	brelse(fibh.sbh);
	brelse(epos.bh);
L
Linus Torvalds 已提交
256 257 258

	return 0;
}