nfsd4: reshuffle lease-setting code to allow reuse
[safe/jmp/linux-2.6] / fs / adfs / dir_f.c
1 /*
2  *  linux/fs/adfs/dir_f.c
3  *
4  * Copyright (C) 1997-1999 Russell King
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  *  E and F format directory handling
11  */
12 #include <linux/buffer_head.h>
13 #include "adfs.h"
14 #include "dir_f.h"
15
16 static void adfs_f_free(struct adfs_dir *dir);
17
18 /*
19  * Read an (unaligned) value of length 1..4 bytes
20  */
21 static inline unsigned int adfs_readval(unsigned char *p, int len)
22 {
23         unsigned int val = 0;
24
25         switch (len) {
26         case 4:         val |= p[3] << 24;
27         case 3:         val |= p[2] << 16;
28         case 2:         val |= p[1] << 8;
29         default:        val |= p[0];
30         }
31         return val;
32 }
33
34 static inline void adfs_writeval(unsigned char *p, int len, unsigned int val)
35 {
36         switch (len) {
37         case 4:         p[3] = val >> 24;
38         case 3:         p[2] = val >> 16;
39         case 2:         p[1] = val >> 8;
40         default:        p[0] = val;
41         }
42 }
43
44 static inline int adfs_readname(char *buf, char *ptr, int maxlen)
45 {
46         char *old_buf = buf;
47
48         while ((unsigned char)*ptr >= ' ' && maxlen--) {
49                 if (*ptr == '/')
50                         *buf++ = '.';
51                 else
52                         *buf++ = *ptr;
53                 ptr++;
54         }
55         *buf = '\0';
56
57         return buf - old_buf;
58 }
59
60 #define ror13(v) ((v >> 13) | (v << 19))
61
62 #define dir_u8(idx)                             \
63         ({ int _buf = idx >> blocksize_bits;    \
64            int _off = idx - (_buf << blocksize_bits);\
65           *(u8 *)(bh[_buf]->b_data + _off);     \
66         })
67
68 #define dir_u32(idx)                            \
69         ({ int _buf = idx >> blocksize_bits;    \
70            int _off = idx - (_buf << blocksize_bits);\
71           *(__le32 *)(bh[_buf]->b_data + _off); \
72         })
73
74 #define bufoff(_bh,_idx)                        \
75         ({ int _buf = _idx >> blocksize_bits;   \
76            int _off = _idx - (_buf << blocksize_bits);\
77           (u8 *)(_bh[_buf]->b_data + _off);     \
78         })
79
80 /*
81  * There are some algorithms that are nice in
82  * assembler, but a bitch in C...  This is one
83  * of them.
84  */
85 static u8
86 adfs_dir_checkbyte(const struct adfs_dir *dir)
87 {
88         struct buffer_head * const *bh = dir->bh;
89         const int blocksize_bits = dir->sb->s_blocksize_bits;
90         union { __le32 *ptr32; u8 *ptr8; } ptr, end;
91         u32 dircheck = 0;
92         int last = 5 - 26;
93         int i = 0;
94
95         /*
96          * Accumulate each word up to the last whole
97          * word of the last directory entry.  This
98          * can spread across several buffer heads.
99          */
100         do {
101                 last += 26;
102                 do {
103                         dircheck = le32_to_cpu(dir_u32(i)) ^ ror13(dircheck);
104
105                         i += sizeof(u32);
106                 } while (i < (last & ~3));
107         } while (dir_u8(last) != 0);
108
109         /*
110          * Accumulate the last few bytes.  These
111          * bytes will be within the same bh.
112          */
113         if (i != last) {
114                 ptr.ptr8 = bufoff(bh, i);
115                 end.ptr8 = ptr.ptr8 + last - i;
116
117                 do {
118                         dircheck = *ptr.ptr8++ ^ ror13(dircheck);
119                 } while (ptr.ptr8 < end.ptr8);
120         }
121
122         /*
123          * The directory tail is in the final bh
124          * Note that contary to the RISC OS PRMs,
125          * the first few bytes are NOT included
126          * in the check.  All bytes are in the
127          * same bh.
128          */
129         ptr.ptr8 = bufoff(bh, 2008);
130         end.ptr8 = ptr.ptr8 + 36;
131
132         do {
133                 __le32 v = *ptr.ptr32++;
134                 dircheck = le32_to_cpu(v) ^ ror13(dircheck);
135         } while (ptr.ptr32 < end.ptr32);
136
137         return (dircheck ^ (dircheck >> 8) ^ (dircheck >> 16) ^ (dircheck >> 24)) & 0xff;
138 }
139
140 /*
141  * Read and check that a directory is valid
142  */
143 static int
144 adfs_dir_read(struct super_block *sb, unsigned long object_id,
145               unsigned int size, struct adfs_dir *dir)
146 {
147         const unsigned int blocksize_bits = sb->s_blocksize_bits;
148         int blk = 0;
149
150         /*
151          * Directories which are not a multiple of 2048 bytes
152          * are considered bad v2 [3.6]
153          */
154         if (size & 2047)
155                 goto bad_dir;
156
157         size >>= blocksize_bits;
158
159         dir->nr_buffers = 0;
160         dir->sb = sb;
161
162         for (blk = 0; blk < size; blk++) {
163                 int phys;
164
165                 phys = __adfs_block_map(sb, object_id, blk);
166                 if (!phys) {
167                         adfs_error(sb, "dir object %lX has a hole at offset %d",
168                                    object_id, blk);
169                         goto release_buffers;
170                 }
171
172                 dir->bh[blk] = sb_bread(sb, phys);
173                 if (!dir->bh[blk])
174                         goto release_buffers;
175         }
176
177         memcpy(&dir->dirhead, bufoff(dir->bh, 0), sizeof(dir->dirhead));
178         memcpy(&dir->dirtail, bufoff(dir->bh, 2007), sizeof(dir->dirtail));
179
180         if (dir->dirhead.startmasseq != dir->dirtail.new.endmasseq ||
181             memcmp(&dir->dirhead.startname, &dir->dirtail.new.endname, 4))
182                 goto bad_dir;
183
184         if (memcmp(&dir->dirhead.startname, "Nick", 4) &&
185             memcmp(&dir->dirhead.startname, "Hugo", 4))
186                 goto bad_dir;
187
188         if (adfs_dir_checkbyte(dir) != dir->dirtail.new.dircheckbyte)
189                 goto bad_dir;
190
191         dir->nr_buffers = blk;
192
193         return 0;
194
195 bad_dir:
196         adfs_error(sb, "corrupted directory fragment %lX",
197                    object_id);
198 release_buffers:
199         for (blk -= 1; blk >= 0; blk -= 1)
200                 brelse(dir->bh[blk]);
201
202         dir->sb = NULL;
203
204         return -EIO;
205 }
206
207 /*
208  * convert a disk-based directory entry to a Linux ADFS directory entry
209  */
210 static inline void
211 adfs_dir2obj(struct object_info *obj, struct adfs_direntry *de)
212 {
213         obj->name_len = adfs_readname(obj->name, de->dirobname, ADFS_F_NAME_LEN);
214         obj->file_id  = adfs_readval(de->dirinddiscadd, 3);
215         obj->loadaddr = adfs_readval(de->dirload, 4);
216         obj->execaddr = adfs_readval(de->direxec, 4);
217         obj->size     = adfs_readval(de->dirlen,  4);
218         obj->attr     = de->newdiratts;
219 }
220
221 /*
222  * convert a Linux ADFS directory entry to a disk-based directory entry
223  */
224 static inline void
225 adfs_obj2dir(struct adfs_direntry *de, struct object_info *obj)
226 {
227         adfs_writeval(de->dirinddiscadd, 3, obj->file_id);
228         adfs_writeval(de->dirload, 4, obj->loadaddr);
229         adfs_writeval(de->direxec, 4, obj->execaddr);
230         adfs_writeval(de->dirlen,  4, obj->size);
231         de->newdiratts = obj->attr;
232 }
233
234 /*
235  * get a directory entry.  Note that the caller is responsible
236  * for holding the relevant locks.
237  */
238 static int
239 __adfs_dir_get(struct adfs_dir *dir, int pos, struct object_info *obj)
240 {
241         struct super_block *sb = dir->sb;
242         struct adfs_direntry de;
243         int thissize, buffer, offset;
244
245         buffer = pos >> sb->s_blocksize_bits;
246
247         if (buffer > dir->nr_buffers)
248                 return -EINVAL;
249
250         offset = pos & (sb->s_blocksize - 1);
251         thissize = sb->s_blocksize - offset;
252         if (thissize > 26)
253                 thissize = 26;
254
255         memcpy(&de, dir->bh[buffer]->b_data + offset, thissize);
256         if (thissize != 26)
257                 memcpy(((char *)&de) + thissize, dir->bh[buffer + 1]->b_data,
258                        26 - thissize);
259
260         if (!de.dirobname[0])
261                 return -ENOENT;
262
263         adfs_dir2obj(obj, &de);
264
265         return 0;
266 }
267
268 static int
269 __adfs_dir_put(struct adfs_dir *dir, int pos, struct object_info *obj)
270 {
271         struct super_block *sb = dir->sb;
272         struct adfs_direntry de;
273         int thissize, buffer, offset;
274
275         buffer = pos >> sb->s_blocksize_bits;
276
277         if (buffer > dir->nr_buffers)
278                 return -EINVAL;
279
280         offset = pos & (sb->s_blocksize - 1);
281         thissize = sb->s_blocksize - offset;
282         if (thissize > 26)
283                 thissize = 26;
284
285         /*
286          * Get the entry in total
287          */
288         memcpy(&de, dir->bh[buffer]->b_data + offset, thissize);
289         if (thissize != 26)
290                 memcpy(((char *)&de) + thissize, dir->bh[buffer + 1]->b_data,
291                        26 - thissize);
292
293         /*
294          * update it
295          */
296         adfs_obj2dir(&de, obj);
297
298         /*
299          * Put the new entry back
300          */
301         memcpy(dir->bh[buffer]->b_data + offset, &de, thissize);
302         if (thissize != 26)
303                 memcpy(dir->bh[buffer + 1]->b_data, ((char *)&de) + thissize,
304                        26 - thissize);
305
306         return 0;
307 }
308
309 /*
310  * the caller is responsible for holding the necessary
311  * locks.
312  */
313 static int
314 adfs_dir_find_entry(struct adfs_dir *dir, unsigned long object_id)
315 {
316         int pos, ret;
317
318         ret = -ENOENT;
319
320         for (pos = 5; pos < ADFS_NUM_DIR_ENTRIES * 26 + 5; pos += 26) {
321                 struct object_info obj;
322
323                 if (!__adfs_dir_get(dir, pos, &obj))
324                         break;
325
326                 if (obj.file_id == object_id) {
327                         ret = pos;
328                         break;
329                 }
330         }
331
332         return ret;
333 }
334
335 static int
336 adfs_f_read(struct super_block *sb, unsigned int id, unsigned int sz, struct adfs_dir *dir)
337 {
338         int ret;
339
340         if (sz != ADFS_NEWDIR_SIZE)
341                 return -EIO;
342
343         ret = adfs_dir_read(sb, id, sz, dir);
344         if (ret)
345                 adfs_error(sb, "unable to read directory");
346         else
347                 dir->parent_id = adfs_readval(dir->dirtail.new.dirparent, 3);
348
349         return ret;
350 }
351
352 static int
353 adfs_f_setpos(struct adfs_dir *dir, unsigned int fpos)
354 {
355         if (fpos >= ADFS_NUM_DIR_ENTRIES)
356                 return -ENOENT;
357
358         dir->pos = 5 + fpos * 26;
359         return 0;
360 }
361
362 static int
363 adfs_f_getnext(struct adfs_dir *dir, struct object_info *obj)
364 {
365         unsigned int ret;
366
367         ret = __adfs_dir_get(dir, dir->pos, obj);
368         if (ret == 0)
369                 dir->pos += 26;
370
371         return ret;
372 }
373
374 static int
375 adfs_f_update(struct adfs_dir *dir, struct object_info *obj)
376 {
377         struct super_block *sb = dir->sb;
378         int ret, i;
379
380         ret = adfs_dir_find_entry(dir, obj->file_id);
381         if (ret < 0) {
382                 adfs_error(dir->sb, "unable to locate entry to update");
383                 goto out;
384         }
385
386         __adfs_dir_put(dir, ret, obj);
387  
388         /*
389          * Increment directory sequence number
390          */
391         dir->bh[0]->b_data[0] += 1;
392         dir->bh[dir->nr_buffers - 1]->b_data[sb->s_blocksize - 6] += 1;
393
394         ret = adfs_dir_checkbyte(dir);
395         /*
396          * Update directory check byte
397          */
398         dir->bh[dir->nr_buffers - 1]->b_data[sb->s_blocksize - 1] = ret;
399
400 #if 1
401         {
402         const unsigned int blocksize_bits = sb->s_blocksize_bits;
403
404         memcpy(&dir->dirhead, bufoff(dir->bh, 0), sizeof(dir->dirhead));
405         memcpy(&dir->dirtail, bufoff(dir->bh, 2007), sizeof(dir->dirtail));
406
407         if (dir->dirhead.startmasseq != dir->dirtail.new.endmasseq ||
408             memcmp(&dir->dirhead.startname, &dir->dirtail.new.endname, 4))
409                 goto bad_dir;
410
411         if (memcmp(&dir->dirhead.startname, "Nick", 4) &&
412             memcmp(&dir->dirhead.startname, "Hugo", 4))
413                 goto bad_dir;
414
415         if (adfs_dir_checkbyte(dir) != dir->dirtail.new.dircheckbyte)
416                 goto bad_dir;
417         }
418 #endif
419         for (i = dir->nr_buffers - 1; i >= 0; i--)
420                 mark_buffer_dirty(dir->bh[i]);
421
422         ret = 0;
423 out:
424         return ret;
425 #if 1
426 bad_dir:
427         adfs_error(dir->sb, "whoops!  I broke a directory!");
428         return -EIO;
429 #endif
430 }
431
432 static int
433 adfs_f_sync(struct adfs_dir *dir)
434 {
435         int err = 0;
436         int i;
437
438         for (i = dir->nr_buffers - 1; i >= 0; i--) {
439                 struct buffer_head *bh = dir->bh[i];
440                 sync_dirty_buffer(bh);
441                 if (buffer_req(bh) && !buffer_uptodate(bh))
442                         err = -EIO;
443         }
444
445         return err;
446 }
447
448 static void
449 adfs_f_free(struct adfs_dir *dir)
450 {
451         int i;
452
453         for (i = dir->nr_buffers - 1; i >= 0; i--) {
454                 brelse(dir->bh[i]);
455                 dir->bh[i] = NULL;
456         }
457
458         dir->nr_buffers = 0;
459         dir->sb = NULL;
460 }
461
462 struct adfs_dir_ops adfs_f_dir_ops = {
463         .read           = adfs_f_read,
464         .setpos         = adfs_f_setpos,
465         .getnext        = adfs_f_getnext,
466         .update         = adfs_f_update,
467         .sync           = adfs_f_sync,
468         .free           = adfs_f_free
469 };