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

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

98 99 100
static int
do_udf_readdir(struct inode *dir, struct file *filp, filldir_t filldir,
	       void *dirent)
L
Linus Torvalds 已提交
101 102
{
	struct udf_fileident_bh fibh;
103
	struct fileIdentDesc *fi = NULL;
L
Linus Torvalds 已提交
104 105 106 107 108 109 110 111 112
	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 已提交
113 114 115
	struct buffer_head *tmp, *bha[16];
	kernel_lb_addr eloc;
	uint32_t elen;
116
	sector_t offset;
L
Linus Torvalds 已提交
117 118
	int i, num;
	unsigned int dt_type;
119
	struct extent_position epos = { NULL, 0, {0, 0} };
L
Linus Torvalds 已提交
120 121 122 123 124 125 126

	if (nf_pos >= size)
		return 0;

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

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

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

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

171
	while (nf_pos < size) {
L
Linus Torvalds 已提交
172 173
		filp->f_pos = nf_pos + 1;

174 175 176
		fi = udf_fileident_read(dir, &nf_pos, &fibh, &cfi, &epos, &eloc,
					&elen, &offset);
		if (!fi) {
L
Linus Torvalds 已提交
177
			if (fibh.sbh != fibh.ebh)
J
Jan Kara 已提交
178 179 180
				brelse(fibh.ebh);
			brelse(fibh.sbh);
			brelse(epos.bh);
L
Linus Torvalds 已提交
181 182 183 184 185 186
			return 0;
		}

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

187
		if (fibh.sbh == fibh.ebh) {
L
Linus Torvalds 已提交
188
			nameptr = fi->fileIdent + liu;
189
		} else {
L
Linus Torvalds 已提交
190 191
			int poffset;	/* Unpaded ending offset */

192
			poffset = fibh.soffset + sizeof(struct fileIdentDesc) + liu + lfi;
L
Linus Torvalds 已提交
193

194 195 196
			if (poffset >= lfi) {
				nameptr = (char *)(fibh.ebh->b_data + poffset - lfi);
			} else {
L
Linus Torvalds 已提交
197
				nameptr = fname;
198 199 200 201
				memcpy(nameptr, fi->fileIdent + liu,
				       lfi - poffset);
				memcpy(nameptr + lfi - poffset,
				       fibh.ebh->b_data, poffset);
L
Linus Torvalds 已提交
202 203 204
			}
		}

205 206
		if ((cfi.fileCharacteristics & FID_FILE_CHAR_DELETED) != 0) {
			if (!UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNDELETE))
L
Linus Torvalds 已提交
207 208
				continue;
		}
209 210 211

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

215
		if (cfi.fileCharacteristics & FID_FILE_CHAR_PARENT) {
J
Josef Sipek 已提交
216
			iblock = parent_ino(filp->f_path.dentry);
L
Linus Torvalds 已提交
217 218 219
			flen = 2;
			memcpy(fname, "..", flen);
			dt_type = DT_DIR;
220
		} else {
L
Linus Torvalds 已提交
221 222 223 224 225 226 227
			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;
		}

228
		if (flen) {
229
			if (filldir(dirent, fname, flen, filp->f_pos, iblock, dt_type) < 0) {
L
Linus Torvalds 已提交
230
				if (fibh.sbh != fibh.ebh)
J
Jan Kara 已提交
231 232 233
					brelse(fibh.ebh);
				brelse(fibh.sbh);
				brelse(epos.bh);
234
	 			return 0;
L
Linus Torvalds 已提交
235 236
			}
		}
237
	} /* end while */
L
Linus Torvalds 已提交
238 239 240 241

	filp->f_pos = nf_pos + 1;

	if (fibh.sbh != fibh.ebh)
J
Jan Kara 已提交
242 243 244
		brelse(fibh.ebh);
	brelse(fibh.sbh);
	brelse(epos.bh);
L
Linus Torvalds 已提交
245 246 247

	return 0;
}