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