Line data Source code
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 : */
17 : static int
18 : isofs_cmp(struct dentry *dentry, const char *compare, int dlen)
19 : {
20 8 : struct qstr qstr;
21 8 :
22 16 : if (!compare)
23 8 : return 1;
24 :
25 : /* check special "." and ".." files */
26 16 : if (dlen == 1) {
27 : /* "." */
28 16 : if (compare[0] == 0) {
29 16 : if (!dentry->d_name.len)
30 8 : return 0;
31 8 : compare = ".";
32 16 : } else if (compare[0] == 1) {
33 8 : compare = "..";
34 8 : dlen = 2;
35 : }
36 : }
37 :
38 8 : qstr.name = compare;
39 8 : qstr.len = dlen;
40 48 : 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 : */
49 : static unsigned long
50 : isofs_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 2 : unsigned long bufsize = ISOFS_BUFFER_SIZE(dir);
55 2 : unsigned char bufbits = ISOFS_BUFFER_BITS(dir);
56 1 : unsigned long block, f_pos, offset, block_saved, offset_saved;
57 2 : struct buffer_head *bh = NULL;
58 4 : struct isofs_sb_info *sbi = ISOFS_SB(dir->i_sb);
59 1 :
60 5 : if (!ISOFS_I(dir)->i_first_extent)
61 2 : return 0;
62 1 :
63 2 : f_pos = 0;
64 2 : offset = 0;
65 2 : block = 0;
66 1 :
67 5 : while (f_pos < dir->i_size) {
68 2 : struct iso_directory_record *de;
69 2 : int de_len, match, i, dlen;
70 1 : char *dpnt;
71 1 :
72 3 : if (!bh) {
73 3 : bh = isofs_bread(dir, block);
74 2 : if (!bh)
75 1 : return 0;
76 : }
77 :
78 2 : de = (struct iso_directory_record *) (bh->b_data + offset);
79 :
80 2 : de_len = *(unsigned char *) de;
81 4 : if (!de_len) {
82 4 : brelse(bh);
83 1 : bh = NULL;
84 1 : f_pos = (f_pos + ISOFS_BLOCK_SIZE) & ~(ISOFS_BLOCK_SIZE - 1);
85 1 : block = f_pos >> bufbits;
86 1 : offset = 0;
87 1 : continue;
88 : }
89 :
90 2 : block_saved = bh->b_blocknr;
91 2 : offset_saved = offset;
92 2 : offset += de_len;
93 2 : f_pos += de_len;
94 :
95 : /* Make sure we have a full directory entry */
96 4 : if (offset >= bufsize) {
97 2 : int slop = bufsize - offset + de_len;
98 2 : memcpy(tmpde, de, slop);
99 2 : offset &= bufsize - 1;
100 2 : block++;
101 4 : brelse(bh);
102 1 : bh = NULL;
103 2 : if (offset) {
104 3 : bh = isofs_bread(dir, block);
105 2 : if (!bh)
106 1 : return 0;
107 2 : memcpy((void *) tmpde + slop, bh->b_data, offset);
108 : }
109 2 : de = tmpde;
110 : }
111 :
112 4 : dlen = de->name_len[0];
113 4 : dpnt = de->name;
114 : /* Basic sanity check, whether name doesn't exceed dir entry */
115 8 : if (de_len < dlen + sizeof(struct iso_directory_record)) {
116 4 : printk(KERN_NOTICE "iso9660: Corrupted directory entry"
117 : " in block %lu of inode %lu\n", block,
118 : dir->i_ino);
119 4 : return 0;
120 : }
121 :
122 35 : if (sbi->s_rock &&
123 : ((i = get_rock_ridge_filename(de, tmpname, dir)))) {
124 1 : dlen = i; /* possibly -1 */
125 5 : dpnt = tmpname;
126 : #ifdef CONFIG_JOLIET
127 15 : } else if (sbi->s_joliet_level) {
128 10 : dlen = get_joliet_filename(de, tmpname, dir);
129 1 : dpnt = tmpname;
130 : #endif
131 15 : } else if (sbi->s_mapping == 'a') {
132 10 : dlen = get_acorn_filename(de, tmpname, dir);
133 1 : dpnt = tmpname;
134 15 : } else if (sbi->s_mapping == 'n') {
135 10 : dlen = isofs_name_translate(de, tmpname, dir);
136 1 : dpnt = tmpname;
137 : }
138 :
139 : /*
140 : * Skip hidden or associated files unless hide or showassoc,
141 : * respectively, is set
142 : */
143 8 : match = 0;
144 96 : if (dlen > 0 &&
145 : (!sbi->s_hide ||
146 : (!(de->flags[-sbi->s_high_sierra] & 1))) &&
147 : (sbi->s_showassoc ||
148 : (!(de->flags[-sbi->s_high_sierra] & 4)))) {
149 17 : match = (isofs_cmp(dentry, dpnt, dlen) == 0);
150 : }
151 18 : if (match) {
152 27 : isofs_normalize_block_and_offset(de,
153 : &block_saved,
154 : &offset_saved);
155 1 : *block_rv = block_saved;
156 1 : *offset_rv = offset_saved;
157 2 : brelse(bh);
158 1 : return 1;
159 : }
160 : }
161 2 : brelse(bh);
162 10 : return 0;
163 : }
164 :
165 : struct dentry *isofs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
166 : {
167 1 : int found;
168 2 : unsigned long uninitialized_var(block);
169 2 : unsigned long uninitialized_var(offset);
170 1 : struct inode *inode;
171 1 : struct page *page;
172 1 :
173 2 : dentry->d_op = dir->i_sb->s_root->d_op;
174 1 :
175 3 : page = alloc_page(GFP_USER);
176 3 : if (!page)
177 4 : return ERR_PTR(-ENOMEM);
178 1 :
179 1 : lock_kernel();
180 13 : found = isofs_find_entry(dir, dentry,
181 : &block, &offset,
182 : page_address(page),
183 : 1024 + page_address(page));
184 1 : __free_page(page);
185 :
186 1 : inode = NULL;
187 2 : if (found) {
188 6 : inode = isofs_iget(dir->i_sb, block, offset);
189 4 : if (IS_ERR(inode)) {
190 1 : unlock_kernel();
191 3 : return ERR_CAST(inode);
192 : }
193 : }
194 2 : unlock_kernel();
195 4 : return d_splice_alias(inode, dentry);
196 : }
|