1 /*
2  *  linux/fs/isofs/dir.c
3  *
4  *  (C) 1992, 1993, 1994  Eric Youngdale Modified for ISO 9660 filesystem.
5  *
6  *  (C) 1991  Linus Torvalds - minix filesystem
7  *
8  *  Steve Beynon		       : Missing last directory entries fixed
9  *  (stephen@askone.demon.co.uk)      : 21st June 1996
10  *
11  *  isofs directory handling functions
12  */
13 #include <linux/errno.h>
14 #include <linux/fs.h>
15 #include <linux/iso_fs.h>
16 #include <linux/kernel.h>
17 #include <linux/stat.h>
18 #include <linux/string.h>
19 #include <linux/mm.h>
20 #include <linux/slab.h>
21 #include <linux/sched.h>
22 #include <linux/locks.h>
23 #include <linux/config.h>
24 
25 #include <asm/uaccess.h>
26 
27 static int isofs_readdir(struct file *, void *, filldir_t);
28 
29 struct file_operations isofs_dir_operations =
30 {
31 	read:		generic_read_dir,
32 	readdir:	isofs_readdir,
33 };
34 
35 /*
36  * directories can handle most operations...
37  */
38 struct inode_operations isofs_dir_inode_operations =
39 {
40 	lookup:		isofs_lookup,
41 };
42 
isofs_name_translate(struct iso_directory_record * de,char * new,struct inode * inode)43 int isofs_name_translate(struct iso_directory_record *de, char *new, struct inode *inode)
44 {
45 	char * old = de->name;
46 	int len = de->name_len[0];
47 	int i;
48 
49 	for (i = 0; i < len; i++) {
50 		unsigned char c = old[i];
51 		if (!c)
52 			break;
53 
54 		if (c >= 'A' && c <= 'Z')
55 			c |= 0x20;	/* lower case */
56 
57 		/* Drop trailing '.;1' (ISO 9660:1988 7.5.1 requires period) */
58 		if (c == '.' && i == len - 3 && old[i + 1] == ';' && old[i + 2] == '1')
59 			break;
60 
61 		/* Drop trailing ';1' */
62 		if (c == ';' && i == len - 2 && old[i + 1] == '1')
63 			break;
64 
65 		/* Convert remaining ';' to '.' */
66 		if (c == ';')
67 			c = '.';
68 
69 		new[i] = c;
70 	}
71 	return i;
72 }
73 
74 /* Acorn extensions written by Matthew Wilcox <willy@bofh.ai> 1998 */
get_acorn_filename(struct iso_directory_record * de,char * retname,struct inode * inode)75 int get_acorn_filename(struct iso_directory_record * de,
76 			    char * retname, struct inode * inode)
77 {
78 	int std;
79 	unsigned char * chr;
80 	int retnamlen = isofs_name_translate(de, retname, inode);
81 	if (retnamlen == 0) return 0;
82 	std = sizeof(struct iso_directory_record) + de->name_len[0];
83 	if (std & 1) std++;
84 	if ((*((unsigned char *) de) - std) != 32) return retnamlen;
85 	chr = ((unsigned char *) de) + std;
86 	if (strncmp(chr, "ARCHIMEDES", 10)) return retnamlen;
87 	if ((*retname == '_') && ((chr[19] & 1) == 1)) *retname = '!';
88 	if (((de->flags[0] & 2) == 0) && (chr[13] == 0xff)
89 		&& ((chr[12] & 0xf0) == 0xf0))
90 	{
91 		retname[retnamlen] = ',';
92 		sprintf(retname+retnamlen+1, "%3.3x",
93 			((chr[12] & 0xf) << 8) | chr[11]);
94 		retnamlen += 4;
95 	}
96 	return retnamlen;
97 }
98 
99 /*
100  * This should _really_ be cleaned up some day..
101  */
do_isofs_readdir(struct inode * inode,struct file * filp,void * dirent,filldir_t filldir,char * tmpname,struct iso_directory_record * tmpde)102 static int do_isofs_readdir(struct inode *inode, struct file *filp,
103 		void *dirent, filldir_t filldir,
104 		char * tmpname, struct iso_directory_record * tmpde)
105 {
106 	unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
107 	unsigned char bufbits = ISOFS_BUFFER_BITS(inode);
108 	unsigned int block, offset;
109 	int inode_number = 0;	/* Quiet GCC */
110 	struct buffer_head *bh = NULL;
111 	int len;
112 	int map;
113 	int high_sierra;
114 	int first_de = 1;
115 	char *p = NULL;		/* Quiet GCC */
116 	struct iso_directory_record *de;
117 
118 	offset = filp->f_pos & (bufsize - 1);
119 	block = filp->f_pos >> bufbits;
120 	high_sierra = inode->i_sb->u.isofs_sb.s_high_sierra;
121 
122 	while (filp->f_pos < inode->i_size) {
123 		int de_len;
124 
125 		if (!bh) {
126 			bh = isofs_bread(inode, block);
127 			if (!bh)
128 				return 0;
129 		}
130 
131 		de = (struct iso_directory_record *) (bh->b_data + offset);
132 		if (first_de)
133 			inode_number = (bh->b_blocknr << bufbits) + offset;
134 
135 		de_len = *(unsigned char *) de;
136 
137 		/* If the length byte is zero, we should move on to the next
138 		   CDROM sector.  If we are at the end of the directory, we
139 		   kick out of the while loop. */
140 
141 		if (de_len == 0) {
142 			brelse(bh);
143 			bh = NULL;
144 			filp->f_pos = (filp->f_pos + ISOFS_BLOCK_SIZE) & ~(ISOFS_BLOCK_SIZE - 1);
145 			block = filp->f_pos >> bufbits;
146 			offset = 0;
147 			continue;
148 		}
149 
150 		offset += de_len;
151 
152 		/* Make sure we have a full directory entry */
153 		if (offset >= bufsize) {
154 			int slop = bufsize - offset + de_len;
155 			memcpy(tmpde, de, slop);
156 			offset &= bufsize - 1;
157 			block++;
158 			brelse(bh);
159 			bh = NULL;
160 			if (offset) {
161 				bh = isofs_bread(inode, block);
162 				if (!bh)
163 					return 0;
164 				memcpy((void *) tmpde + slop, bh->b_data, offset);
165 			}
166 			de = tmpde;
167 		}
168 
169 		if (de->flags[-high_sierra] & 0x80) {
170 			first_de = 0;
171 			filp->f_pos += de_len;
172 			continue;
173 		}
174 		first_de = 1;
175 
176 		/* Handle the case of the '.' directory */
177 		if (de->name_len[0] == 1 && de->name[0] == 0) {
178 			if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino, DT_DIR) < 0)
179 				break;
180 			filp->f_pos += de_len;
181 			continue;
182 		}
183 
184 		len = 0;
185 
186 		/* Handle the case of the '..' directory */
187 		if (de->name_len[0] == 1 && de->name[0] == 1) {
188 			inode_number = filp->f_dentry->d_parent->d_inode->i_ino;
189 			if (filldir(dirent, "..", 2, filp->f_pos, inode_number, DT_DIR) < 0)
190 				break;
191 			filp->f_pos += de_len;
192 			continue;
193 		}
194 
195 		/* Handle everything else.  Do name translation if there
196 		   is no Rock Ridge NM field. */
197 		if (inode->i_sb->u.isofs_sb.s_unhide == 'n') {
198 			/* Do not report hidden or associated files */
199 			if (de->flags[-high_sierra] & 5) {
200 				filp->f_pos += de_len;
201 				continue;
202 			}
203 		}
204 
205 		map = 1;
206 		if (inode->i_sb->u.isofs_sb.s_rock) {
207 			len = get_rock_ridge_filename(de, tmpname, inode);
208 			if (len != 0) {		/* may be -1 */
209 				p = tmpname;
210 				map = 0;
211 			}
212 		}
213 		if (map) {
214 #ifdef CONFIG_JOLIET
215 			if (inode->i_sb->u.isofs_sb.s_joliet_level) {
216 				len = get_joliet_filename(de, tmpname, inode);
217 				p = tmpname;
218 			} else
219 #endif
220 			if (inode->i_sb->u.isofs_sb.s_mapping == 'a') {
221 				len = get_acorn_filename(de, tmpname, inode);
222 				p = tmpname;
223 			} else
224 			if (inode->i_sb->u.isofs_sb.s_mapping == 'n') {
225 				len = isofs_name_translate(de, tmpname, inode);
226 				p = tmpname;
227 			} else {
228 				p = de->name;
229 				len = de->name_len[0];
230 			}
231 		}
232 		if (len > 0) {
233 			if (filldir(dirent, p, len, filp->f_pos, inode_number, DT_UNKNOWN) < 0)
234 				break;
235 		}
236 		filp->f_pos += de_len;
237 
238 		continue;
239 	}
240 	if (bh) brelse(bh);
241 	return 0;
242 }
243 
244 /*
245  * Handle allocation of temporary space for name translation and
246  * handling split directory entries.. The real work is done by
247  * "do_isofs_readdir()".
248  */
isofs_readdir(struct file * filp,void * dirent,filldir_t filldir)249 static int isofs_readdir(struct file *filp,
250 		void *dirent, filldir_t filldir)
251 {
252 	int result;
253 	char * tmpname;
254 	struct iso_directory_record * tmpde;
255 	struct inode *inode = filp->f_dentry->d_inode;
256 
257 	tmpname = (char *) __get_free_page(GFP_KERNEL);
258 	if (!tmpname)
259 		return -ENOMEM;
260 	tmpde = (struct iso_directory_record *) (tmpname+1024);
261 
262 	result = do_isofs_readdir(inode, filp, dirent, filldir, tmpname, tmpde);
263 
264 	free_page((unsigned long) tmpname);
265 	return result;
266 }
267