1/*
2 *  linux/fs/isofs/namei.c
3 *
4 *  (C) 1992  Eric Youngdale Modified for ISO 9660 filesystem.
5 *
6 *  (C) 1991  Linus Torvalds - minix filesystem
7 */
8
9#include <linux/smp_lock.h>
10#include "isofs.h"
11
12/*
13 * ok, we cannot use strncmp, as the name is not in our data space.
14 * Thus we'll have to use isofs_match. No big problem. Match also makes
15 * some sanity tests.
16 */
17static int
18isofs_cmp(struct dentry * dentry, const char * compare, int dlen)
19{
20	struct qstr qstr;
21
22	if (!compare)
23		return 1;
24
25	/* check special "." and ".." files */
26	if (dlen == 1) {
27		/* "." */
28		if (compare[0] == 0) {
29			if (!dentry->d_name.len)
30				return 0;
31			compare = ".";
32		} else if (compare[0] == 1) {
33			compare = "..";
34			dlen = 2;
35		}
36	}
37
38	qstr.name = compare;
39	qstr.len = dlen;
40	return dentry->d_op->d_compare(dentry, &dentry->d_name, &qstr);
41}
42
43/*
44 *	isofs_find_entry()
45 *
46 * finds an entry in the specified directory with the wanted name. It
47 * returns the inode number of the found entry, or 0 on error.
48 */
49static unsigned long
50isofs_find_entry(struct inode *dir, struct dentry *dentry,
51	unsigned long *block_rv, unsigned long* offset_rv,
52	char * tmpname, struct iso_directory_record * tmpde)
53{
54	unsigned long bufsize = ISOFS_BUFFER_SIZE(dir);
55	unsigned char bufbits = ISOFS_BUFFER_BITS(dir);
56	unsigned long block, f_pos, offset, block_saved, offset_saved;
57	struct buffer_head * bh = NULL;
58	struct isofs_sb_info *sbi = ISOFS_SB(dir->i_sb);
59
60	if (!ISOFS_I(dir)->i_first_extent)
61		return 0;
62
63	f_pos = 0;
64	offset = 0;
65	block = 0;
66
67	while (f_pos < dir->i_size) {
68		struct iso_directory_record * de;
69		int de_len, match, i, dlen;
70		char *dpnt;
71
72		if (!bh) {
73			bh = isofs_bread(dir, block);
74			if (!bh)
75				return 0;
76		}
77
78		de = (struct iso_directory_record *) (bh->b_data + offset);
79
80		de_len = *(unsigned char *) de;
81		if (!de_len) {
82			brelse(bh);
83			bh = NULL;
84			f_pos = (f_pos + ISOFS_BLOCK_SIZE) & ~(ISOFS_BLOCK_SIZE - 1);
85			block = f_pos >> bufbits;
86			offset = 0;
87			continue;
88		}
89
90		block_saved = bh->b_blocknr;
91		offset_saved = offset;
92		offset += de_len;
93		f_pos += de_len;
94
95		/* Make sure we have a full directory entry */
96		if (offset >= bufsize) {
97			int slop = bufsize - offset + de_len;
98			memcpy(tmpde, de, slop);
99			offset &= bufsize - 1;
100			block++;
101			brelse(bh);
102			bh = NULL;
103			if (offset) {
104				bh = isofs_bread(dir, block);
105				if (!bh)
106					return 0;
107				memcpy((void *) tmpde + slop, bh->b_data, offset);
108			}
109			de = tmpde;
110		}
111
112		dlen = de->name_len[0];
113		dpnt = de->name;
114
115		if (sbi->s_rock &&
116		    ((i = get_rock_ridge_filename(de, tmpname, dir)))) {
117			dlen = i; 	/* possibly -1 */
118			dpnt = tmpname;
119#ifdef CONFIG_JOLIET
120		} else if (sbi->s_joliet_level) {
121			dlen = get_joliet_filename(de, tmpname, dir);
122			dpnt = tmpname;
123#endif
124		} else if (sbi->s_mapping == 'a') {
125			dlen = get_acorn_filename(de, tmpname, dir);
126			dpnt = tmpname;
127		} else if (sbi->s_mapping == 'n') {
128			dlen = isofs_name_translate(de, tmpname, dir);
129			dpnt = tmpname;
130		}
131
132		/*
133		 * Skip hidden or associated files unless hide or showassoc,
134		 * respectively, is set
135		 */
136		match = 0;
137		if (dlen > 0 &&
138			(sbi->s_hide =='n' ||
139				(!(de->flags[-sbi->s_high_sierra] & 1))) &&
140			(sbi->s_showassoc =='y' ||
141				(!(de->flags[-sbi->s_high_sierra] & 4)))) {
142			match = (isofs_cmp(dentry, dpnt, dlen) == 0);
143		}
144		if (match) {
145			isofs_normalize_block_and_offset(de,
146							 &block_saved,
147							 &offset_saved);
148                        *block_rv = block_saved;
149                        *offset_rv = offset_saved;
150			brelse(bh);
151			return 1;
152		}
153	}
154	brelse(bh);
155	return 0;
156}
157
158struct dentry *isofs_lookup(struct inode * dir, struct dentry * dentry, struct nameidata *nd)
159{
160	int found;
161	unsigned long block, offset;
162	struct inode *inode;
163	struct page *page;
164
165	dentry->d_op = dir->i_sb->s_root->d_op;
166
167	page = alloc_page(GFP_USER);
168	if (!page)
169		return ERR_PTR(-ENOMEM);
170
171	lock_kernel();
172	found = isofs_find_entry(dir, dentry,
173				 &block, &offset,
174				 page_address(page),
175				 1024 + page_address(page));
176	__free_page(page);
177
178	inode = NULL;
179	if (found) {
180		inode = isofs_iget(dir->i_sb, block, offset);
181		if (!inode) {
182			unlock_kernel();
183			return ERR_PTR(-EACCES);
184		}
185	}
186	unlock_kernel();
187	return d_splice_alias(inode, dentry);
188}
189