dir.c 5.4 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
/*
 * 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"

39 40
static int do_udf_readdir(struct inode *dir, struct file *filp,
			  filldir_t filldir, void *dirent)
L
Linus Torvalds 已提交
41
{
42
	struct udf_fileident_bh fibh = { .sbh = NULL, .ebh = NULL};
43
	struct fileIdentDesc *fi = NULL;
L
Linus Torvalds 已提交
44 45
	struct fileIdentDesc cfi;
	int block, iblock;
J
Jan Kara 已提交
46
	loff_t nf_pos = (filp->f_pos - 1) << 2;
L
Linus Torvalds 已提交
47
	int flen;
48
	char *fname = NULL;
L
Linus Torvalds 已提交
49 50 51
	char *nameptr;
	uint16_t liu;
	uint8_t lfi;
J
Jan Kara 已提交
52
	loff_t size = udf_ext0_offset(dir) + dir->i_size;
J
Jan Kara 已提交
53 54 55
	struct buffer_head *tmp, *bha[16];
	kernel_lb_addr eloc;
	uint32_t elen;
56
	sector_t offset;
57
	int i, num, ret = 0;
L
Linus Torvalds 已提交
58
	unsigned int dt_type;
59
	struct extent_position epos = { NULL, 0, {0, 0} };
60
	struct udf_inode_info *iinfo;
L
Linus Torvalds 已提交
61 62

	if (nf_pos >= size)
63 64 65 66 67 68 69
		goto out;

	fname = kmalloc(UDF_NAME_LEN, GFP_NOFS);
	if (!fname) {
		ret = -ENOMEM;
		goto out;
	}
L
Linus Torvalds 已提交
70 71

	if (nf_pos == 0)
J
Jan Kara 已提交
72
		nf_pos = udf_ext0_offset(dir);
L
Linus Torvalds 已提交
73

J
Jan Kara 已提交
74
	fibh.soffset = fibh.eoffset = nf_pos & (dir->i_sb->s_blocksize - 1);
75
	iinfo = UDF_I(dir);
76 77 78 79 80 81 82
	if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
		if (inode_bmap(dir, nf_pos >> dir->i_sb->s_blocksize_bits,
		    &epos, &eloc, &elen, &offset)
		    != (EXT_RECORDED_ALLOCATED >> 30)) {
			ret = -ENOENT;
			goto out;
		}
L
Linus Torvalds 已提交
83
		block = udf_get_lb_pblock(dir->i_sb, eloc, offset);
84
		if ((++offset << dir->i_sb->s_blocksize_bits) < elen) {
85
			if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
J
Jan Kara 已提交
86
				epos.offset -= sizeof(short_ad);
87
			else if (iinfo->i_alloc_type ==
88
					ICBTAG_FLAG_AD_LONG)
J
Jan Kara 已提交
89
				epos.offset -= sizeof(long_ad);
90
		} else {
L
Linus Torvalds 已提交
91
			offset = 0;
92
		}
L
Linus Torvalds 已提交
93

94
		if (!(fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block))) {
95 96
			ret = -EIO;
			goto out;
L
Linus Torvalds 已提交
97
		}
98 99

		if (!(offset & ((16 >> (dir->i_sb->s_blocksize_bits - 9)) - 1))) {
L
Linus Torvalds 已提交
100
			i = 16 >> (dir->i_sb->s_blocksize_bits - 9);
101
			if (i + offset > (elen >> dir->i_sb->s_blocksize_bits))
102
				i = (elen >> dir->i_sb->s_blocksize_bits) - offset;
103
			for (num = 0; i > 0; i--) {
104
				block = udf_get_lb_pblock(dir->i_sb, eloc, offset + i);
L
Linus Torvalds 已提交
105
				tmp = udf_tgetblk(dir->i_sb, block);
106
				if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp))
L
Linus Torvalds 已提交
107 108 109 110
					bha[num++] = tmp;
				else
					brelse(tmp);
			}
111
			if (num) {
L
Linus Torvalds 已提交
112
				ll_rw_block(READA, num, bha);
113
				for (i = 0; i < num; i++)
L
Linus Torvalds 已提交
114 115 116 117 118
					brelse(bha[i]);
			}
		}
	}

119
	while (nf_pos < size) {
J
Jan Kara 已提交
120
		filp->f_pos = (nf_pos >> 2) + 1;
L
Linus Torvalds 已提交
121

122 123
		fi = udf_fileident_read(dir, &nf_pos, &fibh, &cfi, &epos, &eloc,
					&elen, &offset);
124 125
		if (!fi)
			goto out;
L
Linus Torvalds 已提交
126 127 128 129

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

130
		if (fibh.sbh == fibh.ebh) {
L
Linus Torvalds 已提交
131
			nameptr = fi->fileIdent + liu;
132
		} else {
L
Linus Torvalds 已提交
133 134
			int poffset;	/* Unpaded ending offset */

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

137 138 139
			if (poffset >= lfi) {
				nameptr = (char *)(fibh.ebh->b_data + poffset - lfi);
			} else {
L
Linus Torvalds 已提交
140
				nameptr = fname;
141 142 143 144
				memcpy(nameptr, fi->fileIdent + liu,
				       lfi - poffset);
				memcpy(nameptr + lfi - poffset,
				       fibh.ebh->b_data, poffset);
L
Linus Torvalds 已提交
145 146 147
			}
		}

148 149
		if ((cfi.fileCharacteristics & FID_FILE_CHAR_DELETED) != 0) {
			if (!UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNDELETE))
L
Linus Torvalds 已提交
150 151
				continue;
		}
152 153 154

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

158
		if (cfi.fileCharacteristics & FID_FILE_CHAR_PARENT) {
J
Josef Sipek 已提交
159
			iblock = parent_ino(filp->f_path.dentry);
L
Linus Torvalds 已提交
160 161 162
			flen = 2;
			memcpy(fname, "..", flen);
			dt_type = DT_DIR;
163
		} else {
L
Linus Torvalds 已提交
164 165 166 167 168 169 170
			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;
		}

171 172 173
		if (flen && filldir(dirent, fname, flen, filp->f_pos,
				    iblock, dt_type) < 0)
			goto out;
174
	} /* end while */
L
Linus Torvalds 已提交
175

J
Jan Kara 已提交
176
	filp->f_pos = (nf_pos >> 2) + 1;
L
Linus Torvalds 已提交
177

178
out:
L
Linus Torvalds 已提交
179
	if (fibh.sbh != fibh.ebh)
J
Jan Kara 已提交
180 181 182
		brelse(fibh.ebh);
	brelse(fibh.sbh);
	brelse(epos.bh);
183
	kfree(fname);
L
Linus Torvalds 已提交
184

185
	return ret;
L
Linus Torvalds 已提交
186
}
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214

static int udf_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
	struct inode *dir = filp->f_path.dentry->d_inode;
	int result;

	lock_kernel();

	if (filp->f_pos == 0) {
		if (filldir(dirent, ".", 1, filp->f_pos, dir->i_ino, DT_DIR) < 0) {
			unlock_kernel();
			return 0;
		}
		filp->f_pos++;
	}

	result = do_udf_readdir(dir, filp, filldir, dirent);
	unlock_kernel();
 	return result;
}

/* readdir and lookup functions */
const struct file_operations udf_dir_operations = {
	.read			= generic_read_dir,
	.readdir		= udf_readdir,
	.ioctl			= udf_ioctl,
	.fsync			= udf_fsync_file,
};