OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [fs/] [adfs/] [dir_f.c] - Blame information for rev 1275

Go to most recent revision | Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.