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/gfp.h>
14#include "isofs.h"
15
16int isofs_name_translate(struct iso_directory_record *de, char *new, struct inode *inode)
17{
18	char * old = de->name;
19	int len = de->name_len[0];
20	int i;
21
22	for (i = 0; i < len; i++) {
23		unsigned char c = old[i];
24		if (!c)
25			break;
26
27		if (c >= 'A' && c <= 'Z')
28			c |= 0x20;	/* lower case */
29
30		/* Drop trailing '.;1' (ISO 9660:1988 7.5.1 requires period) */
31		if (c == '.' && i == len - 3 && old[i + 1] == ';' && old[i + 2] == '1')
32			break;
33
34		/* Drop trailing ';1' */
35		if (c == ';' && i == len - 2 && old[i + 1] == '1')
36			break;
37
38		/* Convert remaining ';' to '.' */
39		/* Also '/' to '.' (broken Acorn-generated ISO9660 images) */
40		if (c == ';' || c == '/')
41			c = '.';
42
43		new[i] = c;
44	}
45	return i;
46}
47
48/* Acorn extensions written by Matthew Wilcox <willy@bofh.ai> 1998 */
49int get_acorn_filename(struct iso_directory_record *de,
50			    char *retname, struct inode *inode)
51{
52	int std;
53	unsigned char *chr;
54	int retnamlen = isofs_name_translate(de, retname, inode);
55
56	if (retnamlen == 0)
57		return 0;
58	std = sizeof(struct iso_directory_record) + de->name_len[0];
59	if (std & 1)
60		std++;
61	if ((*((unsigned char *) de) - std) != 32)
62		return retnamlen;
63	chr = ((unsigned char *) de) + std;
64	if (strncmp(chr, "ARCHIMEDES", 10))
65		return retnamlen;
66	if ((*retname == '_') && ((chr[19] & 1) == 1))
67		*retname = '!';
68	if (((de->flags[0] & 2) == 0) && (chr[13] == 0xff)
69		&& ((chr[12] & 0xf0) == 0xf0)) {
70		retname[retnamlen] = ',';
71		sprintf(retname+retnamlen+1, "%3.3x",
72			((chr[12] & 0xf) << 8) | chr[11]);
73		retnamlen += 4;
74	}
75	return retnamlen;
76}
77
78/*
79 * This should _really_ be cleaned up some day..
80 */
81static int do_isofs_readdir(struct inode *inode, struct file *file,
82		struct dir_context *ctx,
83		char *tmpname, struct iso_directory_record *tmpde)
84{
85	unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
86	unsigned char bufbits = ISOFS_BUFFER_BITS(inode);
87	unsigned long block, offset, block_saved, offset_saved;
88	unsigned long inode_number = 0;	/* Quiet GCC */
89	struct buffer_head *bh = NULL;
90	int len;
91	int map;
92	int first_de = 1;
93	char *p = NULL;		/* Quiet GCC */
94	struct iso_directory_record *de;
95	struct isofs_sb_info *sbi = ISOFS_SB(inode->i_sb);
96
97	offset = ctx->pos & (bufsize - 1);
98	block = ctx->pos >> bufbits;
99
100	while (ctx->pos < inode->i_size) {
101		int de_len;
102
103		if (!bh) {
104			bh = isofs_bread(inode, block);
105			if (!bh)
106				return 0;
107		}
108
109		de = (struct iso_directory_record *) (bh->b_data + offset);
110
111		de_len = *(unsigned char *)de;
112
113		/*
114		 * If the length byte is zero, we should move on to the next
115		 * CDROM sector.  If we are at the end of the directory, we
116		 * kick out of the while loop.
117		 */
118
119		if (de_len == 0) {
120			brelse(bh);
121			bh = NULL;
122			ctx->pos = (ctx->pos + ISOFS_BLOCK_SIZE) & ~(ISOFS_BLOCK_SIZE - 1);
123			block = ctx->pos >> bufbits;
124			offset = 0;
125			continue;
126		}
127
128		block_saved = block;
129		offset_saved = offset;
130		offset += de_len;
131
132		/* Make sure we have a full directory entry */
133		if (offset >= bufsize) {
134			int slop = bufsize - offset + de_len;
135			memcpy(tmpde, de, slop);
136			offset &= bufsize - 1;
137			block++;
138			brelse(bh);
139			bh = NULL;
140			if (offset) {
141				bh = isofs_bread(inode, block);
142				if (!bh)
143					return 0;
144				memcpy((void *) tmpde + slop, bh->b_data, offset);
145			}
146			de = tmpde;
147		}
148		/* Basic sanity check, whether name doesn't exceed dir entry */
149		if (de_len < de->name_len[0] +
150					sizeof(struct iso_directory_record)) {
151			printk(KERN_NOTICE "iso9660: Corrupted directory entry"
152			       " in block %lu of inode %lu\n", block,
153			       inode->i_ino);
154			return -EIO;
155		}
156
157		if (first_de) {
158			isofs_normalize_block_and_offset(de,
159							&block_saved,
160							&offset_saved);
161			inode_number = isofs_get_ino(block_saved,
162							offset_saved, bufbits);
163		}
164
165		if (de->flags[-sbi->s_high_sierra] & 0x80) {
166			first_de = 0;
167			ctx->pos += de_len;
168			continue;
169		}
170		first_de = 1;
171
172		/* Handle the case of the '.' directory */
173		if (de->name_len[0] == 1 && de->name[0] == 0) {
174			if (!dir_emit_dot(file, ctx))
175				break;
176			ctx->pos += de_len;
177			continue;
178		}
179
180		len = 0;
181
182		/* Handle the case of the '..' directory */
183		if (de->name_len[0] == 1 && de->name[0] == 1) {
184			if (!dir_emit_dotdot(file, ctx))
185				break;
186			ctx->pos += de_len;
187			continue;
188		}
189
190		/* Handle everything else.  Do name translation if there
191		   is no Rock Ridge NM field. */
192
193		/*
194		 * Do not report hidden files if so instructed, or associated
195		 * files unless instructed to do so
196		 */
197		if ((sbi->s_hide && (de->flags[-sbi->s_high_sierra] & 1)) ||
198		    (!sbi->s_showassoc &&
199				(de->flags[-sbi->s_high_sierra] & 4))) {
200			ctx->pos += de_len;
201			continue;
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 (!dir_emit(ctx, p, len, inode_number, DT_UNKNOWN))
233				break;
234		}
235		ctx->pos += de_len;
236
237		continue;
238	}
239	if (bh)
240		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 */
249static int isofs_readdir(struct file *file, struct dir_context *ctx)
250{
251	int result;
252	char *tmpname;
253	struct iso_directory_record *tmpde;
254	struct inode *inode = file_inode(file);
255
256	tmpname = (char *)__get_free_page(GFP_KERNEL);
257	if (tmpname == NULL)
258		return -ENOMEM;
259
260	tmpde = (struct iso_directory_record *) (tmpname+1024);
261
262	result = do_isofs_readdir(inode, file, ctx, tmpname, tmpde);
263
264	free_page((unsigned long) tmpname);
265	return result;
266}
267
268const struct file_operations isofs_dir_operations =
269{
270	.llseek = generic_file_llseek,
271	.read = generic_read_dir,
272	.iterate = isofs_readdir,
273};
274
275/*
276 * directories can handle most operations...
277 */
278const struct inode_operations isofs_dir_inode_operations =
279{
280	.lookup = isofs_lookup,
281};
282
283
284