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