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