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
	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} };
120
	struct udf_inode_info *iinfo;
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
	fibh.soffset = fibh.eoffset = (nf_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2;
129 130
	iinfo = UDF_I(dir);
	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
L
Linus Torvalds 已提交
131
		fibh.sbh = fibh.ebh = NULL;
132 133
	} 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 已提交
134
		block = udf_get_lb_pblock(dir->i_sb, eloc, offset);
135
		if ((++offset << dir->i_sb->s_blocksize_bits) < elen) {
136
			if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
J
Jan Kara 已提交
137
				epos.offset -= sizeof(short_ad);
138
			else if (iinfo->i_alloc_type ==
139
					ICBTAG_FLAG_AD_LONG)
J
Jan Kara 已提交
140
				epos.offset -= sizeof(long_ad);
141
		} else {
L
Linus Torvalds 已提交
142
			offset = 0;
143
		}
L
Linus Torvalds 已提交
144

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

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

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

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

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

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

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

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

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

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

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

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

	filp->f_pos = nf_pos + 1;

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

	return 0;
}