Import 2.3.30pre7
[davej-history.git] / fs / adfs / dir.c
blobf3eb4e8722651373b00888efcb5388c505271236
1 /*
2 * linux/fs/adfs/dir.c
4 * Copyright (C) 1997 Russell King
5 */
7 #include <linux/errno.h>
8 #include <linux/fs.h>
9 #include <linux/adfs_fs.h>
10 #include <linux/sched.h>
11 #include <linux/stat.h>
13 static ssize_t adfs_dirread (struct file *filp, char *buf,
14 size_t siz, loff_t *ppos)
16 return -EISDIR;
19 static int adfs_readdir (struct file *, void *, filldir_t);
21 static struct file_operations adfs_dir_operations = {
22 NULL, /* lseek - default */
23 adfs_dirread, /* read */
24 NULL, /* write - bad */
25 adfs_readdir, /* readdir */
26 NULL, /* select - default */
27 NULL, /* ioctl */
28 NULL, /* mmap */
29 NULL, /* no special open code */
30 NULL, /* flush */
31 NULL, /* no special release code */
32 file_fsync, /* fsync */
33 NULL, /* fasync */
34 NULL, /* check_media_change */
35 NULL /* revalidate */
39 * directories can handle most operations...
41 struct inode_operations adfs_dir_inode_operations = {
42 &adfs_dir_operations, /* default directory file-ops */
43 NULL, /* create */
44 adfs_lookup, /* lookup */
45 NULL, /* link */
46 NULL, /* unlink */
47 NULL, /* symlink */
48 NULL, /* mkdir */
49 NULL, /* rmdir */
50 NULL, /* mknod */
51 NULL, /* rename */
52 NULL, /* read link */
53 NULL, /* follow link */
54 NULL, /* get_block */
55 NULL, /* read page */
56 NULL, /* write page */
57 NULL, /* truncate */
58 NULL, /* permission */
59 NULL /* revalidate */
62 unsigned int adfs_val (unsigned char *p, int len)
64 unsigned int val = 0;
66 switch (len) {
67 case 4:
68 val |= p[3] << 24;
69 case 3:
70 val |= p[2] << 16;
71 case 2:
72 val |= p[1] << 8;
73 default:
74 val |= p[0];
76 return val;
79 static unsigned int adfs_filetype (unsigned int load)
81 if ((load & 0xfff00000) != 0xfff00000)
82 return (unsigned int) -1;
83 return (load >> 8) & 0xfff;
86 static unsigned int adfs_time (unsigned int load, unsigned int exec)
88 unsigned int high, low;
90 /* Check for unstamped files. */
91 if ((load & 0xfff00000) != 0xfff00000)
92 return 0;
94 high = ((load << 24) | (exec >> 8));
95 low = exec & 255;
97 /* Files dated pre 1970. */
98 if (high < 0x336e996a)
99 return 0;
101 high -= 0x336e996a;
103 /* Files dated post 2038 ish. */
104 if (high > 0x31ffffff)
105 return 0x7fffffff;
107 /* 65537 = h256,l1
108 * (h256 % 100) = 56 h256 / 100 = 2
109 * 56 << 8 = 14336 2 * 256 = 512
110 * + l1 = 14337
111 * / 100 = 143
112 * + 512 = 655
114 return (((high % 100) << 8) + low) / 100 + (high / 100 << 8);
117 int adfs_readname (char *buf, char *ptr, int maxlen)
119 int size = 0;
120 while (*ptr >= ' ' && maxlen--) {
121 switch (*ptr) {
122 case '/':
123 *buf++ = '.';
124 break;
125 default:
126 *buf++ = *ptr;
127 break;
129 ptr++;
130 size ++;
132 *buf = '\0';
133 return size;
136 int adfs_dir_read_parent (struct inode *inode, struct buffer_head **bhp)
138 struct super_block *sb;
139 int i, size;
141 sb = inode->i_sb;
143 size = 2048 >> sb->s_blocksize_bits;
145 for (i = 0; i < size; i++) {
146 int block;
148 block = adfs_parent_bmap (inode, i);
149 if (block)
150 bhp[i] = bread (sb->s_dev, block, sb->s_blocksize);
151 else
152 adfs_error (sb, "adfs_dir_read_parent",
153 "directory %lu with a hole at offset %d", inode->i_ino, i);
154 if (!block || !bhp[i]) {
155 int j;
156 for (j = i - 1; j >= 0; j--)
157 brelse (bhp[j]);
158 return 0;
161 return i;
164 int adfs_dir_read (struct inode *inode, struct buffer_head **bhp)
166 struct super_block *sb;
167 int i, size;
169 if (!inode || !S_ISDIR(inode->i_mode))
170 return 0;
172 sb = inode->i_sb;
174 size = inode->i_size >> sb->s_blocksize_bits;
176 for (i = 0; i < size; i++) {
177 int block;
179 block = adfs_bmap (inode, i);
180 if (block)
181 bhp[i] = bread (sb->s_dev, block, sb->s_blocksize);
182 else
183 adfs_error (sb, "adfs_dir_read",
184 "directory %lX,%lX with a hole at offset %d",
185 inode->i_ino, inode->u.adfs_i.file_id, i);
186 if (!block || !bhp[i]) {
187 int j;
188 for (j = i - 1; j >= 0; j--)
189 brelse (bhp[j]);
190 return 0;
193 return i;
196 int adfs_dir_check (struct inode *inode, struct buffer_head **bhp, int buffers, union adfs_dirtail *dtp)
198 struct adfs_dirheader dh;
199 union adfs_dirtail dt;
201 memcpy (&dh, bhp[0]->b_data, sizeof (dh));
202 memcpy (&dt, bhp[3]->b_data + 471, sizeof(dt));
204 if (memcmp (&dh.startmasseq, &dt.new.endmasseq, 5) ||
205 (memcmp (&dh.startname, "Nick", 4) &&
206 memcmp (&dh.startname, "Hugo", 4))) {
207 adfs_error (inode->i_sb, "adfs_check_dir",
208 "corrupted directory inode %lX,%lX",
209 inode->i_ino, inode->u.adfs_i.file_id);
210 return 1;
212 if (dtp)
213 *dtp = dt;
214 return 0;
217 void adfs_dir_free (struct buffer_head **bhp, int buffers)
219 int i;
221 for (i = buffers - 1; i >= 0; i--)
222 brelse (bhp[i]);
225 /* convert a disk-based directory entry to a Linux ADFS directory entry */
226 static inline void
227 adfs_dirent_to_idirent(struct adfs_idir_entry *ide, struct adfs_direntry *de)
229 ide->name_len = adfs_readname(ide->name, de->dirobname, ADFS_NAME_LEN);
230 ide->file_id = adfs_val(de->dirinddiscadd, 3);
231 ide->size = adfs_val(de->dirlen, 4);
232 ide->mode = de->newdiratts;
233 ide->mtime = adfs_time(adfs_val(de->dirload, 4), adfs_val(de->direxec, 4));
234 ide->filetype = adfs_filetype(adfs_val(de->dirload, 4));
237 int adfs_dir_get (struct super_block *sb, struct buffer_head **bhp,
238 int buffers, int pos, unsigned long parent_object_id,
239 struct adfs_idir_entry *ide)
241 struct adfs_direntry de;
242 int thissize, buffer, offset;
244 offset = pos & (sb->s_blocksize - 1);
245 buffer = pos >> sb->s_blocksize_bits;
247 if (buffer > buffers)
248 return 0;
250 thissize = sb->s_blocksize - offset;
251 if (thissize > 26)
252 thissize = 26;
254 memcpy (&de, bhp[buffer]->b_data + offset, thissize);
255 if (thissize != 26)
256 memcpy (((char *)&de) + thissize, bhp[buffer + 1]->b_data, 26 - thissize);
258 if (!de.dirobname[0])
259 return 0;
261 ide->inode_no = adfs_inode_generate (parent_object_id, pos);
262 adfs_dirent_to_idirent(ide, &de);
263 return 1;
266 int adfs_dir_find_entry (struct super_block *sb, struct buffer_head **bhp,
267 int buffers, unsigned int pos,
268 struct adfs_idir_entry *ide)
270 struct adfs_direntry de;
271 int offset, buffer, thissize;
273 offset = pos & (sb->s_blocksize - 1);
274 buffer = pos >> sb->s_blocksize_bits;
276 if (buffer > buffers)
277 return 0;
279 thissize = sb->s_blocksize - offset;
280 if (thissize > 26)
281 thissize = 26;
283 memcpy (&de, bhp[buffer]->b_data + offset, thissize);
284 if (thissize != 26)
285 memcpy (((char *)&de) + thissize, bhp[buffer + 1]->b_data, 26 - thissize);
287 if (!de.dirobname[0])
288 return 0;
290 adfs_dirent_to_idirent(ide, &de);
291 return 1;
294 static int adfs_readdir (struct file *filp, void *dirent, filldir_t filldir)
296 struct inode *inode = filp->f_dentry->d_inode;
297 struct super_block *sb;
298 struct buffer_head *bh[4];
299 union adfs_dirtail dt;
300 unsigned long parent_object_id, dir_object_id;
301 int buffers, pos;
303 sb = inode->i_sb;
305 if (filp->f_pos > ADFS_NUM_DIR_ENTRIES + 2)
306 return -ENOENT;
308 if (!(buffers = adfs_dir_read (inode, bh))) {
309 adfs_error (sb, "adfs_readdir", "unable to read directory");
310 return -EINVAL;
313 if (adfs_dir_check (inode, bh, buffers, &dt)) {
314 adfs_dir_free (bh, buffers);
315 return -ENOENT;
318 parent_object_id = adfs_val (dt.new.dirparent, 3);
319 dir_object_id = adfs_inode_objid (inode);
321 if (filp->f_pos < 2) {
322 if (filp->f_pos < 1) {
323 if (filldir (dirent, ".", 1, 0, inode->i_ino) < 0)
324 return 0;
325 filp->f_pos ++;
327 if (filldir (dirent, "..", 2, 1,
328 adfs_inode_generate (parent_object_id, 0)) < 0)
329 return 0;
330 filp->f_pos ++;
333 pos = 5 + (filp->f_pos - 2) * 26;
334 while (filp->f_pos < 79) {
335 struct adfs_idir_entry ide;
337 if (!adfs_dir_get (sb, bh, buffers, pos, dir_object_id, &ide))
338 break;
340 if (filldir (dirent, ide.name, ide.name_len, filp->f_pos, ide.inode_no) < 0)
341 return 0;
342 filp->f_pos ++;
343 pos += 26;
345 adfs_dir_free (bh, buffers);
346 return 0;