1 /*
2  * dir.c
3  *
4  * PURPOSE
5  *  Directory handling routines for the OSTA-UDF(tm) filesystem.
6  *
7  * CONTACTS
8  *	E-mail regarding any portion of the Linux UDF file system should be
9  *	directed to the development team mailing list (run by majordomo):
10  *		linux_udf@hpesjro.fc.hp.com
11  *
12  * COPYRIGHT
13  *	This file is distributed under the terms of the GNU General Public
14  *	License (GPL). Copies of the GPL can be obtained from:
15  *		ftp://prep.ai.mit.edu/pub/gnu/GPL
16  *	Each contributing author retains all rights to their own work.
17  *
18  *  (C) 1998-2001 Ben Fennema
19  *
20  * HISTORY
21  *
22  *  10/05/98 dgb  Split directory operations into it's own file
23  *                Implemented directory reads via do_udf_readdir
24  *  10/06/98      Made directory operations work!
25  *  11/17/98      Rewrote directory to support ICBTAG_FLAG_AD_LONG
26  *  11/25/98 blf  Rewrote directory handling (readdir+lookup) to support reading
27  *                across blocks.
28  *  12/12/98      Split out the lookup code to namei.c. bulk of directory
29  *                code now in directory.c:udf_fileident_read.
30  */
31 
32 #include "udfdecl.h"
33 
34 #include <linux/string.h>
35 #include <linux/errno.h>
36 #include <linux/mm.h>
37 #include <linux/slab.h>
38 
39 #include "udf_i.h"
40 #include "udf_sb.h"
41 
42 /* Prototypes for file operations */
43 static int udf_readdir(struct file *, void *, filldir_t);
44 static int do_udf_readdir(struct inode *, struct file *, filldir_t, void *);
45 
46 /* readdir and lookup functions */
47 
48 struct file_operations udf_dir_operations = {
49 	read:			generic_read_dir,
50 	readdir:		udf_readdir,
51 	ioctl:			udf_ioctl,
52 	fsync:			udf_fsync_file,
53 };
54 
55 /*
56  * udf_readdir
57  *
58  * PURPOSE
59  *	Read a directory entry.
60  *
61  * DESCRIPTION
62  *	Optional - sys_getdents() will return -ENOTDIR if this routine is not
63  *	available.
64  *
65  *	Refer to sys_getdents() in fs/readdir.c
66  *	sys_getdents() -> .
67  *
68  * PRE-CONDITIONS
69  *	filp			Pointer to directory file.
70  *	buf			Pointer to directory entry buffer.
71  *	filldir			Pointer to filldir function.
72  *
73  * POST-CONDITIONS
74  *	<return>		>=0 on success.
75  *
76  * HISTORY
77  *	July 1, 1997 - Andrew E. Mileski
78  *	Written, tested, and released.
79  */
80 
udf_readdir(struct file * filp,void * dirent,filldir_t filldir)81 int udf_readdir(struct file *filp, void *dirent, filldir_t filldir)
82 {
83 	struct inode *dir = filp->f_dentry->d_inode;
84 	int result;
85 
86 	if ( filp->f_pos == 0 )
87 	{
88 		if (filldir(dirent, ".", 1, filp->f_pos, dir->i_ino, DT_DIR) < 0)
89 			return 0;
90 		filp->f_pos ++;
91 	}
92 
93 	result = do_udf_readdir(dir, filp, filldir, dirent);
94 	UPDATE_ATIME(dir);
95  	return result;
96 }
97 
98 static int
do_udf_readdir(struct inode * dir,struct file * filp,filldir_t filldir,void * dirent)99 do_udf_readdir(struct inode * dir, struct file *filp, filldir_t filldir, void *dirent)
100 {
101 	struct udf_fileident_bh fibh;
102 	struct fileIdentDesc *fi=NULL;
103 	struct fileIdentDesc cfi;
104 	int block, iblock;
105 	loff_t nf_pos = filp->f_pos - 1;
106 	int flen;
107 	char fname[255];
108 	char *nameptr;
109 	uint16_t liu;
110 	uint8_t lfi;
111 	loff_t size = (udf_ext0_offset(dir) + dir->i_size) >> 2;
112 	struct buffer_head * bh = NULL, * tmp, * bha[16];
113 	lb_addr bloc, eloc;
114 	uint32_t extoffset, elen, offset;
115 	int i, num;
116 	unsigned int dt_type;
117 
118 	if (nf_pos >= size)
119 		return 0;
120 
121 	if (nf_pos == 0)
122 		nf_pos = (udf_ext0_offset(dir) >> 2);
123 
124 	fibh.soffset = fibh.eoffset = (nf_pos & ((dir->i_sb->s_blocksize - 1) >> 2)) << 2;
125 	if (inode_bmap(dir, nf_pos >> (dir->i_sb->s_blocksize_bits - 2),
126 		&bloc, &extoffset, &eloc, &elen, &offset, &bh) == (EXT_RECORDED_ALLOCATED >> 30))
127 	{
128 		offset >>= dir->i_sb->s_blocksize_bits;
129 		block = udf_get_lb_pblock(dir->i_sb, eloc, offset);
130 		if ((++offset << dir->i_sb->s_blocksize_bits) < elen)
131 		{
132 			if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_SHORT)
133 				extoffset -= sizeof(short_ad);
134 			else if (UDF_I_ALLOCTYPE(dir) == ICBTAG_FLAG_AD_LONG)
135 				extoffset -= sizeof(long_ad);
136 		}
137 		else
138 			offset = 0;
139 	}
140 	else
141 	{
142 		udf_release_data(bh);
143 		return -ENOENT;
144 	}
145 
146 	if (!(fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block)))
147 	{
148 		udf_release_data(bh);
149 		return -EIO;
150 	}
151 
152 	if (!(offset & ((16 >> (dir->i_sb->s_blocksize_bits - 9))-1)))
153 	{
154 		i = 16 >> (dir->i_sb->s_blocksize_bits - 9);
155 		if (i+offset > (elen >> dir->i_sb->s_blocksize_bits))
156 			i = (elen >> dir->i_sb->s_blocksize_bits)-offset;
157 		for (num=0; i>0; i--)
158 		{
159 			block = udf_get_lb_pblock(dir->i_sb, eloc, offset+i);
160 			tmp = udf_tgetblk(dir->i_sb, block);
161 			if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp))
162 				bha[num++] = tmp;
163 			else
164 				brelse(tmp);
165 		}
166 		if (num)
167 		{
168 			ll_rw_block(READA, num, bha);
169 			for (i=0; i<num; i++)
170 				brelse(bha[i]);
171 		}
172 	}
173 
174 	while ( nf_pos < size )
175 	{
176 		filp->f_pos = nf_pos + 1;
177 
178 		fi = udf_fileident_read(dir, &nf_pos, &fibh, &cfi, &bloc, &extoffset, &eloc, &elen, &offset, &bh);
179 
180 		if (!fi)
181 		{
182 			if (fibh.sbh != fibh.ebh)
183 				udf_release_data(fibh.ebh);
184 			udf_release_data(fibh.sbh);
185 			udf_release_data(bh);
186 			return 0;
187 		}
188 
189 		liu = le16_to_cpu(cfi.lengthOfImpUse);
190 		lfi = cfi.lengthFileIdent;
191 
192 		if (fibh.sbh == fibh.ebh)
193 			nameptr = fi->fileIdent + liu;
194 		else
195 		{
196 			int poffset;	/* Unpaded ending offset */
197 
198 			poffset = fibh.soffset + sizeof(struct fileIdentDesc) + liu + lfi;
199 
200 			if (poffset >= lfi)
201 				nameptr = (char *)(fibh.ebh->b_data + poffset - lfi);
202 			else
203 			{
204 				nameptr = fname;
205 				memcpy(nameptr, fi->fileIdent + liu, lfi - poffset);
206 				memcpy(nameptr + lfi - poffset, fibh.ebh->b_data, poffset);
207 			}
208 		}
209 
210 		if ( (cfi.fileCharacteristics & FID_FILE_CHAR_DELETED) != 0 )
211 		{
212 			if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNDELETE) )
213 				continue;
214 		}
215 
216 		if ( (cfi.fileCharacteristics & FID_FILE_CHAR_HIDDEN) != 0 )
217 		{
218 			if ( !UDF_QUERY_FLAG(dir->i_sb, UDF_FLAG_UNHIDE) )
219 				continue;
220 		}
221 
222 		if ( cfi.fileCharacteristics & FID_FILE_CHAR_PARENT )
223 		{
224 			iblock = udf_get_lb_pblock(dir->i_sb, UDF_I_LOCATION(filp->f_dentry->d_parent->d_inode), 0);
225 			flen = 2;
226 			memcpy(fname, "..", flen);
227 			dt_type = DT_DIR;
228 		}
229 		else
230 		{
231 			iblock = udf_get_lb_pblock(dir->i_sb, lelb_to_cpu(cfi.icb.extLocation), 0);
232 			flen = udf_get_filename(dir->i_sb, nameptr, fname, lfi);
233 			dt_type = DT_UNKNOWN;
234 		}
235 
236 		if (flen)
237 		{
238 			if (filldir(dirent, fname, flen, filp->f_pos, iblock, dt_type) < 0)
239 			{
240 				if (fibh.sbh != fibh.ebh)
241 					udf_release_data(fibh.ebh);
242 				udf_release_data(fibh.sbh);
243 				udf_release_data(bh);
244 	 			return 0;
245 			}
246 		}
247 	} /* end while */
248 
249 	filp->f_pos = nf_pos + 1;
250 
251 	if (fibh.sbh != fibh.ebh)
252 		udf_release_data(fibh.ebh);
253 	udf_release_data(fibh.sbh);
254 	udf_release_data(bh);
255 
256 	return 0;
257 }
258