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