* better
[mascara-docs.git] / i386 / linux-2.3.21 / fs / adfs / dir.c
blob0d0d11a06f1640566c3d99767cb920ec68b8e52e
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, /* flush page */
58 NULL, /* truncate */
59 NULL, /* permission */
60 NULL, /* smap */
61 NULL /* revalidate */
64 unsigned int adfs_val (unsigned char *p, int len)
66 unsigned int val = 0;
68 switch (len) {
69 case 4:
70 val |= p[3] << 24;
71 case 3:
72 val |= p[2] << 16;
73 case 2:
74 val |= p[1] << 8;
75 default:
76 val |= p[0];
78 return val;
81 static unsigned int adfs_filetype (unsigned int load)
83 if ((load & 0xfff00000) != 0xfff00000)
84 return (unsigned int) -1;
85 return (load >> 8) & 0xfff;
88 static unsigned int adfs_time (unsigned int load, unsigned int exec)
90 unsigned int high, low;
92 /* Check for unstamped files. */
93 if ((load & 0xfff00000) != 0xfff00000)
94 return 0;
96 high = ((load << 24) | (exec >> 8));
97 low = exec & 255;
99 /* Files dated pre 1970. */
100 if (high < 0x336e996a)
101 return 0;
103 high -= 0x336e996a;
105 /* Files dated post 2038 ish. */
106 if (high > 0x31ffffff)
107 return 0x7fffffff;
109 /* 65537 = h256,l1
110 * (h256 % 100) = 56 h256 / 100 = 2
111 * 56 << 8 = 14336 2 * 256 = 512
112 * + l1 = 14337
113 * / 100 = 143
114 * + 512 = 655
116 return (((high % 100) << 8) + low) / 100 + (high / 100 << 8);
119 int adfs_readname (char *buf, char *ptr, int maxlen)
121 int size = 0;
122 while (*ptr >= ' ' && maxlen--) {
123 switch (*ptr) {
124 case '/':
125 *buf++ = '.';
126 break;
127 default:
128 *buf++ = *ptr;
129 break;
131 ptr++;
132 size ++;
134 *buf = '\0';
135 return size;
138 int adfs_dir_read_parent (struct inode *inode, struct buffer_head **bhp)
140 struct super_block *sb;
141 int i, size;
143 sb = inode->i_sb;
145 size = 2048 >> sb->s_blocksize_bits;
147 for (i = 0; i < size; i++) {
148 int block;
150 block = adfs_parent_bmap (inode, i);
151 if (block)
152 bhp[i] = bread (sb->s_dev, block, sb->s_blocksize);
153 else
154 adfs_error (sb, "adfs_dir_read_parent",
155 "directory %lu with a hole at offset %d", inode->i_ino, i);
156 if (!block || !bhp[i]) {
157 int j;
158 for (j = i - 1; j >= 0; j--)
159 brelse (bhp[j]);
160 return 0;
163 return i;
166 int adfs_dir_read (struct inode *inode, struct buffer_head **bhp)
168 struct super_block *sb;
169 int i, size;
171 if (!inode || !S_ISDIR(inode->i_mode))
172 return 0;
174 sb = inode->i_sb;
176 size = inode->i_size >> sb->s_blocksize_bits;
178 for (i = 0; i < size; i++) {
179 int block;
181 block = adfs_bmap (inode, i);
182 if (block)
183 bhp[i] = bread (sb->s_dev, block, sb->s_blocksize);
184 else
185 adfs_error (sb, "adfs_dir_read",
186 "directory %lX,%lX with a hole at offset %d",
187 inode->i_ino, inode->u.adfs_i.file_id, i);
188 if (!block || !bhp[i]) {
189 int j;
190 for (j = i - 1; j >= 0; j--)
191 brelse (bhp[j]);
192 return 0;
195 return i;
198 int adfs_dir_check (struct inode *inode, struct buffer_head **bhp, int buffers, union adfs_dirtail *dtp)
200 struct adfs_dirheader dh;
201 union adfs_dirtail dt;
203 memcpy (&dh, bhp[0]->b_data, sizeof (dh));
204 memcpy (&dt, bhp[3]->b_data + 471, sizeof(dt));
206 if (memcmp (&dh.startmasseq, &dt.new.endmasseq, 5) ||
207 (memcmp (&dh.startname, "Nick", 4) &&
208 memcmp (&dh.startname, "Hugo", 4))) {
209 adfs_error (inode->i_sb, "adfs_check_dir",
210 "corrupted directory inode %lX,%lX",
211 inode->i_ino, inode->u.adfs_i.file_id);
212 return 1;
214 if (dtp)
215 *dtp = dt;
216 return 0;
219 void adfs_dir_free (struct buffer_head **bhp, int buffers)
221 int i;
223 for (i = buffers - 1; i >= 0; i--)
224 brelse (bhp[i]);
227 /* convert a disk-based directory entry to a Linux ADFS directory entry */
228 static inline void
229 adfs_dirent_to_idirent(struct adfs_idir_entry *ide, struct adfs_direntry *de)
231 ide->name_len = adfs_readname(ide->name, de->dirobname, ADFS_NAME_LEN);
232 ide->file_id = adfs_val(de->dirinddiscadd, 3);
233 ide->size = adfs_val(de->dirlen, 4);
234 ide->mode = de->newdiratts;
235 ide->mtime = adfs_time(adfs_val(de->dirload, 4), adfs_val(de->direxec, 4));
236 ide->filetype = adfs_filetype(adfs_val(de->dirload, 4));
239 int adfs_dir_get (struct super_block *sb, struct buffer_head **bhp,
240 int buffers, int pos, unsigned long parent_object_id,
241 struct adfs_idir_entry *ide)
243 struct adfs_direntry de;
244 int thissize, buffer, offset;
246 offset = pos & (sb->s_blocksize - 1);
247 buffer = pos >> sb->s_blocksize_bits;
249 if (buffer > buffers)
250 return 0;
252 thissize = sb->s_blocksize - offset;
253 if (thissize > 26)
254 thissize = 26;
256 memcpy (&de, bhp[buffer]->b_data + offset, thissize);
257 if (thissize != 26)
258 memcpy (((char *)&de) + thissize, bhp[buffer + 1]->b_data, 26 - thissize);
260 if (!de.dirobname[0])
261 return 0;
263 ide->inode_no = adfs_inode_generate (parent_object_id, pos);
264 adfs_dirent_to_idirent(ide, &de);
265 return 1;
268 int adfs_dir_find_entry (struct super_block *sb, struct buffer_head **bhp,
269 int buffers, unsigned int pos,
270 struct adfs_idir_entry *ide)
272 struct adfs_direntry de;
273 int offset, buffer, thissize;
275 offset = pos & (sb->s_blocksize - 1);
276 buffer = pos >> sb->s_blocksize_bits;
278 if (buffer > buffers)
279 return 0;
281 thissize = sb->s_blocksize - offset;
282 if (thissize > 26)
283 thissize = 26;
285 memcpy (&de, bhp[buffer]->b_data + offset, thissize);
286 if (thissize != 26)
287 memcpy (((char *)&de) + thissize, bhp[buffer + 1]->b_data, 26 - thissize);
289 if (!de.dirobname[0])
290 return 0;
292 adfs_dirent_to_idirent(ide, &de);
293 return 1;
296 static int adfs_readdir (struct file *filp, void *dirent, filldir_t filldir)
298 struct inode *inode = filp->f_dentry->d_inode;
299 struct super_block *sb;
300 struct buffer_head *bh[4];
301 union adfs_dirtail dt;
302 unsigned long parent_object_id, dir_object_id;
303 int buffers, pos;
305 sb = inode->i_sb;
307 if (filp->f_pos > ADFS_NUM_DIR_ENTRIES + 2)
308 return -ENOENT;
310 if (!(buffers = adfs_dir_read (inode, bh))) {
311 adfs_error (sb, "adfs_readdir", "unable to read directory");
312 return -EINVAL;
315 if (adfs_dir_check (inode, bh, buffers, &dt)) {
316 adfs_dir_free (bh, buffers);
317 return -ENOENT;
320 parent_object_id = adfs_val (dt.new.dirparent, 3);
321 dir_object_id = adfs_inode_objid (inode);
323 if (filp->f_pos < 2) {
324 if (filp->f_pos < 1) {
325 if (filldir (dirent, ".", 1, 0, inode->i_ino) < 0)
326 return 0;
327 filp->f_pos ++;
329 if (filldir (dirent, "..", 2, 1,
330 adfs_inode_generate (parent_object_id, 0)) < 0)
331 return 0;
332 filp->f_pos ++;
335 pos = 5 + (filp->f_pos - 2) * 26;
336 while (filp->f_pos < 79) {
337 struct adfs_idir_entry ide;
339 if (!adfs_dir_get (sb, bh, buffers, pos, dir_object_id, &ide))
340 break;
342 if (filldir (dirent, ide.name, ide.name_len, filp->f_pos, ide.inode_no) < 0)
343 return 0;
344 filp->f_pos ++;
345 pos += 26;
347 adfs_dir_free (bh, buffers);
348 return 0;