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