xref: /openbmc/linux/fs/isofs/dir.c (revision 87c2ce3b)
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 
197 		/*
198 		 * Do not report hidden files if so instructed, or associated
199 		 * files unless instructed to do so
200 		 */
201 		if ((sbi->s_hide == 'y' &&
202 				(de->flags[-sbi->s_high_sierra] & 1)) ||
203 		      (sbi->s_showassoc =='n' &&
204 				(de->flags[-sbi->s_high_sierra] & 4))) {
205 			filp->f_pos += de_len;
206 			continue;
207 		}
208 
209 		map = 1;
210 		if (sbi->s_rock) {
211 			len = get_rock_ridge_filename(de, tmpname, inode);
212 			if (len != 0) {		/* may be -1 */
213 				p = tmpname;
214 				map = 0;
215 			}
216 		}
217 		if (map) {
218 #ifdef CONFIG_JOLIET
219 			if (sbi->s_joliet_level) {
220 				len = get_joliet_filename(de, tmpname, inode);
221 				p = tmpname;
222 			} else
223 #endif
224 			if (sbi->s_mapping == 'a') {
225 				len = get_acorn_filename(de, tmpname, inode);
226 				p = tmpname;
227 			} else
228 			if (sbi->s_mapping == 'n') {
229 				len = isofs_name_translate(de, tmpname, inode);
230 				p = tmpname;
231 			} else {
232 				p = de->name;
233 				len = de->name_len[0];
234 			}
235 		}
236 		if (len > 0) {
237 			if (filldir(dirent, p, len, filp->f_pos, inode_number, DT_UNKNOWN) < 0)
238 				break;
239 		}
240 		filp->f_pos += de_len;
241 
242 		continue;
243 	}
244 	if (bh) brelse(bh);
245 	return 0;
246 }
247 
248 /*
249  * Handle allocation of temporary space for name translation and
250  * handling split directory entries.. The real work is done by
251  * "do_isofs_readdir()".
252  */
253 static int isofs_readdir(struct file *filp,
254 		void *dirent, filldir_t filldir)
255 {
256 	int result;
257 	char * tmpname;
258 	struct iso_directory_record * tmpde;
259 	struct inode *inode = filp->f_dentry->d_inode;
260 
261 
262 	tmpname = (char *)__get_free_page(GFP_KERNEL);
263 	if (tmpname == NULL)
264 		return -ENOMEM;
265 
266 	lock_kernel();
267 	tmpde = (struct iso_directory_record *) (tmpname+1024);
268 
269 	result = do_isofs_readdir(inode, filp, dirent, filldir, tmpname, tmpde);
270 
271 	free_page((unsigned long) tmpname);
272 	unlock_kernel();
273 	return result;
274 }
275