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