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 128
	fibh.soffset = fibh.eoffset = (nf_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2;
	if (UDF_I_ALLOCTYPE(dir) == 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) {
L
Linus Torvalds 已提交
134
			if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT)
J
Jan Kara 已提交
135
				epos.offset -= sizeof(short_ad);
L
Linus Torvalds 已提交
136
			else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG)
J
Jan Kara 已提交
137
				epos.offset -= sizeof(long_ad);
138
		} else {
L
Linus Torvalds 已提交
139
			offset = 0;
140
		}
L
Linus Torvalds 已提交
141

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

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

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

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

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

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

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

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

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

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

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

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

	filp->f_pos = nf_pos + 1;

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

	return 0;
}