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

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [fs/] [affs/] [amigaffs.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/*
2
 *  linux/fs/affs/amigaffs.c
3
 *
4
 *  (c) 1996  Hans-Joachim Widmaier - Rewritten
5
 *
6
 *  (C) 1993  Ray Burr - Amiga FFS filesystem.
7
 *
8
 *  Please send bug reports to: hjw@zvw.de
9
 */
10
 
11
#include <stdarg.h>
12
#include <linux/stat.h>
13
#include <linux/sched.h>
14
#include <linux/affs_fs.h>
15
#include <linux/string.h>
16
#include <linux/locks.h>
17
#include <linux/mm.h>
18
#include <linux/amigaffs.h>
19
 
20
extern struct timezone sys_tz;
21
 
22
static char ErrorBuffer[256];
23
 
24
/*
25
 * Functions for accessing Amiga-FFS structures.
26
 */
27
 
28
 
29
/* Insert a header block bh into the directory dir
30
 * caller must hold AFFS_DIR->i_hash_lock!
31
 */
32
 
33
int
34
affs_insert_hash(struct inode *dir, struct buffer_head *bh)
35
{
36
        struct super_block *sb = dir->i_sb;
37
        struct buffer_head *dir_bh;
38
        u32 ino, hash_ino;
39
        int offset;
40
 
41
        ino = bh->b_blocknr;
42
        offset = affs_hash_name(sb, AFFS_TAIL(sb, bh)->name + 1, AFFS_TAIL(sb, bh)->name[0]);
43
 
44
        pr_debug("AFFS: insert_hash(dir=%u, ino=%d)\n", (u32)dir->i_ino, ino);
45
 
46
        dir_bh = affs_bread(sb, dir->i_ino);
47
        if (!dir_bh)
48
                return -EIO;
49
 
50
        hash_ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[offset]);
51
        while (hash_ino) {
52
                affs_brelse(dir_bh);
53
                dir_bh = affs_bread(sb, hash_ino);
54
                if (!dir_bh)
55
                        return -EIO;
56
                hash_ino = be32_to_cpu(AFFS_TAIL(sb, dir_bh)->hash_chain);
57
        }
58
        AFFS_TAIL(sb, bh)->parent = cpu_to_be32(dir->i_ino);
59
        AFFS_TAIL(sb, bh)->hash_chain = 0;
60
        affs_fix_checksum(sb, bh);
61
 
62
        if (dir->i_ino == dir_bh->b_blocknr)
63
                AFFS_HEAD(dir_bh)->table[offset] = cpu_to_be32(ino);
64
        else
65
                AFFS_TAIL(sb, dir_bh)->hash_chain = cpu_to_be32(ino);
66
 
67
        affs_adjust_checksum(dir_bh, ino);
68
        mark_buffer_dirty_inode(dir_bh, dir);
69
        affs_brelse(dir_bh);
70
 
71
        dir->i_mtime = dir->i_ctime = CURRENT_TIME;
72
        dir->i_version = ++event;
73
        mark_inode_dirty(dir);
74
 
75
        return 0;
76
}
77
 
78
/* Remove a header block from its directory.
79
 * caller must hold AFFS_DIR->i_hash_lock!
80
 */
81
 
82
int
83
affs_remove_hash(struct inode *dir, struct buffer_head *rem_bh)
84
{
85
        struct super_block *sb;
86
        struct buffer_head *bh;
87
        u32 rem_ino, hash_ino, ino;
88
        int offset, retval;
89
 
90
        sb = dir->i_sb;
91
        rem_ino = rem_bh->b_blocknr;
92
        offset = affs_hash_name(sb, AFFS_TAIL(sb, rem_bh)->name+1, AFFS_TAIL(sb, rem_bh)->name[0]);
93
        pr_debug("AFFS: remove_hash(dir=%d, ino=%d, hashval=%d)\n", (u32)dir->i_ino, rem_ino, offset);
94
 
95
        bh = affs_bread(sb, dir->i_ino);
96
        if (!bh)
97
                return -EIO;
98
 
99
        retval = -ENOENT;
100
        hash_ino = be32_to_cpu(AFFS_HEAD(bh)->table[offset]);
101
        while (hash_ino) {
102
                if (hash_ino == rem_ino) {
103
                        ino = AFFS_TAIL(sb, rem_bh)->hash_chain;
104
                        if (dir->i_ino == bh->b_blocknr)
105
                                AFFS_HEAD(bh)->table[offset] = ino;
106
                        else
107
                                AFFS_TAIL(sb, bh)->hash_chain = ino;
108
                        affs_adjust_checksum(bh, be32_to_cpu(ino) - hash_ino);
109
                        mark_buffer_dirty_inode(bh, dir);
110
                        AFFS_TAIL(sb, rem_bh)->parent = 0;
111
                        retval = 0;
112
                        break;
113
                }
114
                affs_brelse(bh);
115
                bh = affs_bread(sb, hash_ino);
116
                if (!bh)
117
                        return -EIO;
118
                hash_ino = be32_to_cpu(AFFS_TAIL(sb, bh)->hash_chain);
119
        }
120
 
121
        affs_brelse(bh);
122
 
123
        dir->i_mtime = dir->i_ctime = CURRENT_TIME;
124
        dir->i_version = ++event;
125
        mark_inode_dirty(dir);
126
 
127
        return retval;
128
}
129
 
130
static void
131
affs_fix_dcache(struct dentry *dentry, u32 entry_ino)
132
{
133
        struct inode *inode = dentry->d_inode;
134
        void *data = dentry->d_fsdata;
135
        struct list_head *head, *next;
136
 
137
        spin_lock(&dcache_lock);
138
        head = &inode->i_dentry;
139
        next = head->next;
140
        while (next != head) {
141
                dentry = list_entry(next, struct dentry, d_alias);
142
                if (entry_ino == (u32)(long)dentry->d_fsdata) {
143
                        dentry->d_fsdata = data;
144
                        break;
145
                }
146
                next = next->next;
147
        }
148
        spin_unlock(&dcache_lock);
149
}
150
 
151
 
152
/* Remove header from link chain */
153
 
154
static int
155
affs_remove_link(struct dentry *dentry)
156
{
157
        struct inode *dir, *inode = dentry->d_inode;
158
        struct super_block *sb = inode->i_sb;
159
        struct buffer_head *bh = NULL, *link_bh = NULL;
160
        u32 link_ino, ino;
161
        int retval;
162
 
163
        pr_debug("AFFS: remove_link(key=%ld)\n", inode->i_ino);
164
        retval = -EIO;
165
        bh = affs_bread(sb, inode->i_ino);
166
        if (!bh)
167
                goto done;
168
 
169
        link_ino = (u32)(long)dentry->d_fsdata;
170
        if (inode->i_ino == link_ino) {
171
                /* we can't remove the head of the link, as its blocknr is still used as ino,
172
                 * so we remove the block of the first link instead.
173
                 */
174
                link_ino = be32_to_cpu(AFFS_TAIL(sb, bh)->link_chain);
175
                link_bh = affs_bread(sb, link_ino);
176
                if (!link_bh)
177
                        goto done;
178
 
179
                dir = iget(sb, be32_to_cpu(AFFS_TAIL(sb, link_bh)->parent));
180
                if (!dir)
181
                        goto done;
182
 
183
                affs_lock_dir(dir);
184
                affs_fix_dcache(dentry, link_ino);
185
                retval = affs_remove_hash(dir, link_bh);
186
                if (retval)
187
                        goto done;
188
                mark_buffer_dirty_inode(link_bh, inode);
189
 
190
                memcpy(AFFS_TAIL(sb, bh)->name, AFFS_TAIL(sb, link_bh)->name, 32);
191
                retval = affs_insert_hash(dir, bh);
192
                if (retval)
193
                        goto done;
194
                mark_buffer_dirty_inode(bh, inode);
195
 
196
                affs_unlock_dir(dir);
197
                iput(dir);
198
        } else {
199
                link_bh = affs_bread(sb, link_ino);
200
                if (!link_bh)
201
                        goto done;
202
        }
203
 
204
        while ((ino = be32_to_cpu(AFFS_TAIL(sb, bh)->link_chain))) {
205
                if (ino == link_ino) {
206
                        ino = AFFS_TAIL(sb, link_bh)->link_chain;
207
                        AFFS_TAIL(sb, bh)->link_chain = ino;
208
                        affs_adjust_checksum(bh, be32_to_cpu(ino) - link_ino);
209
                        mark_buffer_dirty_inode(bh, inode);
210
                        retval = 0;
211
                        /* Fix the link count, if bh is a normal header block without links */
212
                        switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) {
213
                        case ST_LINKDIR:
214
                        case ST_LINKFILE:
215
                                break;
216
                        default:
217
                                if (!AFFS_TAIL(sb, bh)->link_chain)
218
                                        inode->i_nlink = 1;
219
                        }
220
                        affs_free_block(sb, link_ino);
221
                        goto done;
222
                }
223
                affs_brelse(bh);
224
                bh = affs_bread(sb, ino);
225
                if (!bh)
226
                        goto done;
227
        }
228
        retval = -ENOENT;
229
done:
230
        affs_brelse(link_bh);
231
        affs_brelse(bh);
232
        return retval;
233
}
234
 
235
 
236
static int
237
affs_empty_dir(struct inode *inode)
238
{
239
        struct super_block *sb = inode->i_sb;
240
        struct buffer_head *bh;
241
        int retval, size;
242
 
243
        retval = -EIO;
244
        bh = affs_bread(sb, inode->i_ino);
245
        if (!bh)
246
                goto done;
247
 
248
        retval = -ENOTEMPTY;
249
        for (size = AFFS_SB->s_hashsize - 1; size >= 0; size--)
250
                if (AFFS_HEAD(bh)->table[size])
251
                        goto not_empty;
252
        retval = 0;
253
not_empty:
254
        affs_brelse(bh);
255
done:
256
        return retval;
257
}
258
 
259
 
260
/* Remove a filesystem object. If the object to be removed has
261
 * links to it, one of the links must be changed to inherit
262
 * the file or directory. As above, any inode will do.
263
 * The buffer will not be freed. If the header is a link, the
264
 * block will be marked as free.
265
 * This function returns a negative error number in case of
266
 * an error, else 0 if the inode is to be deleted or 1 if not.
267
 */
268
 
269
int
270
affs_remove_header(struct dentry *dentry)
271
{
272
        struct super_block *sb;
273
        struct inode *inode, *dir;
274
        struct buffer_head *bh = NULL;
275
        int retval;
276
 
277
        dir = dentry->d_parent->d_inode;
278
        sb = dir->i_sb;
279
 
280
        retval = -ENOENT;
281
        inode = dentry->d_inode;
282
        if (!inode)
283
                goto done;
284
 
285
        pr_debug("AFFS: remove_header(key=%ld)\n", inode->i_ino);
286
        retval = -EIO;
287
        bh = affs_bread(sb, (u32)(long)dentry->d_fsdata);
288
        if (!bh)
289
                goto done;
290
 
291
        affs_lock_link(inode);
292
        affs_lock_dir(dir);
293
        switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) {
294
        case ST_USERDIR:
295
                /* if we ever want to support links to dirs
296
                 * i_hash_lock of the inode must only be
297
                 * taken after some checks
298
                 */
299
                affs_lock_dir(inode);
300
                retval = affs_empty_dir(inode);
301
                affs_unlock_dir(inode);
302
                if (retval)
303
                        goto done_unlock;
304
                break;
305
        default:
306
                break;
307
        }
308
 
309
        retval = affs_remove_hash(dir, bh);
310
        if (retval)
311
                goto done_unlock;
312
        mark_buffer_dirty_inode(bh, inode);
313
 
314
        affs_unlock_dir(dir);
315
 
316
        if (inode->i_nlink > 1)
317
                retval = affs_remove_link(dentry);
318
        else
319
                inode->i_nlink = 0;
320
        affs_unlock_link(inode);
321
        inode->i_ctime = CURRENT_TIME;
322
        mark_inode_dirty(inode);
323
 
324
done:
325
        affs_brelse(bh);
326
        return retval;
327
 
328
done_unlock:
329
        affs_unlock_dir(dir);
330
        affs_unlock_link(inode);
331
        goto done;
332
}
333
 
334
/* Checksum a block, do various consistency checks and optionally return
335
   the blocks type number.  DATA points to the block.  If their pointers
336
   are non-null, *PTYPE and *STYPE are set to the primary and secondary
337
   block types respectively, *HASHSIZE is set to the size of the hashtable
338
   (which lets us calculate the block size).
339
   Returns non-zero if the block is not consistent. */
340
 
341
u32
342
affs_checksum_block(struct super_block *sb, struct buffer_head *bh)
343
{
344
        u32 *ptr = (u32 *)bh->b_data;
345
        u32 sum;
346
        int bsize;
347
 
348
        sum = 0;
349
        for (bsize = sb->s_blocksize / sizeof(u32); bsize > 0; bsize--)
350
                sum += be32_to_cpu(*ptr++);
351
        return sum;
352
}
353
 
354
/*
355
 * Calculate the checksum of a disk block and store it
356
 * at the indicated position.
357
 */
358
 
359
void
360
affs_fix_checksum(struct super_block *sb, struct buffer_head *bh)
361
{
362
        int cnt = sb->s_blocksize / sizeof(u32);
363
        u32 *ptr = (u32 *)bh->b_data;
364
        u32 checksum, *checksumptr;
365
 
366
        checksumptr = ptr + 5;
367
        *checksumptr = 0;
368
        for (checksum = 0; cnt > 0; ptr++, cnt--)
369
                checksum += be32_to_cpu(*ptr);
370
        *checksumptr = cpu_to_be32(-checksum);
371
}
372
 
373
void
374
secs_to_datestamp(time_t secs, struct affs_date *ds)
375
{
376
        u32      days;
377
        u32      minute;
378
 
379
        secs -= sys_tz.tz_minuteswest * 60 + ((8 * 365 + 2) * 24 * 60 * 60);
380
        if (secs < 0)
381
                secs = 0;
382
        days    = secs / 86400;
383
        secs   -= days * 86400;
384
        minute  = secs / 60;
385
        secs   -= minute * 60;
386
 
387
        ds->days = be32_to_cpu(days);
388
        ds->mins = be32_to_cpu(minute);
389
        ds->ticks = be32_to_cpu(secs * 50);
390
}
391
 
392
mode_t
393
prot_to_mode(u32 prot)
394
{
395
        int mode = 0;
396
 
397
        if (!(prot & FIBF_NOWRITE))
398
                mode |= S_IWUSR;
399
        if (!(prot & FIBF_NOREAD))
400
                mode |= S_IRUSR;
401
        if (!(prot & FIBF_NOEXECUTE))
402
                mode |= S_IXUSR;
403
        if (prot & FIBF_GRP_WRITE)
404
                mode |= S_IWGRP;
405
        if (prot & FIBF_GRP_READ)
406
                mode |= S_IRGRP;
407
        if (prot & FIBF_GRP_EXECUTE)
408
                mode |= S_IXGRP;
409
        if (prot & FIBF_OTR_WRITE)
410
                mode |= S_IWOTH;
411
        if (prot & FIBF_OTR_READ)
412
                mode |= S_IROTH;
413
        if (prot & FIBF_OTR_EXECUTE)
414
                mode |= S_IXOTH;
415
 
416
        return mode;
417
}
418
 
419
void
420
mode_to_prot(struct inode *inode)
421
{
422
        u32 prot = AFFS_INODE->i_protect;
423
        mode_t mode = inode->i_mode;
424
 
425
        if (!(mode & S_IXUSR))
426
                prot |= FIBF_NOEXECUTE;
427
        if (!(mode & S_IRUSR))
428
                prot |= FIBF_NOREAD;
429
        if (!(mode & S_IWUSR))
430
                prot |= FIBF_NOWRITE;
431
        if (mode & S_IXGRP)
432
                prot |= FIBF_GRP_EXECUTE;
433
        if (mode & S_IRGRP)
434
                prot |= FIBF_GRP_READ;
435
        if (mode & S_IWGRP)
436
                prot |= FIBF_GRP_WRITE;
437
        if (mode & S_IXOTH)
438
                prot |= FIBF_OTR_EXECUTE;
439
        if (mode & S_IROTH)
440
                prot |= FIBF_OTR_READ;
441
        if (mode & S_IWOTH)
442
                prot |= FIBF_OTR_WRITE;
443
 
444
        AFFS_INODE->i_protect = prot;
445
}
446
 
447
void
448
affs_error(struct super_block *sb, const char *function, const char *fmt, ...)
449
{
450
        va_list  args;
451
 
452
        va_start(args,fmt);
453
        vsprintf(ErrorBuffer,fmt,args);
454
        va_end(args);
455
 
456
        printk(KERN_CRIT "AFFS error (device %s): %s(): %s\n", bdevname(sb->s_dev),
457
                function,ErrorBuffer);
458
        if (!(sb->s_flags & MS_RDONLY))
459
                printk(KERN_WARNING "AFFS: Remounting filesystem read-only\n");
460
        sb->s_flags |= MS_RDONLY;
461
        AFFS_SB->s_flags |= SF_READONLY;        /* Don't allow to remount rw */
462
}
463
 
464
void
465
affs_warning(struct super_block *sb, const char *function, const char *fmt, ...)
466
{
467
        va_list  args;
468
 
469
        va_start(args,fmt);
470
        vsprintf(ErrorBuffer,fmt,args);
471
        va_end(args);
472
 
473
        printk(KERN_WARNING "AFFS warning (device %s): %s(): %s\n", bdevname(sb->s_dev),
474
                function,ErrorBuffer);
475
}
476
 
477
/* Check if the name is valid for a affs object. */
478
 
479
int
480
affs_check_name(const unsigned char *name, int len)
481
{
482
        int      i;
483
 
484
        if (len > 30)
485
#ifdef AFFS_NO_TRUNCATE
486
                return -ENAMETOOLONG;
487
#else
488
                len = 30;
489
#endif
490
 
491
        for (i = 0; i < len; i++) {
492
                if (name[i] < ' ' || name[i] == ':'
493
                    || (name[i] > 0x7e && name[i] < 0xa0))
494
                        return -EINVAL;
495
        }
496
 
497
        return 0;
498
}
499
 
500
/* This function copies name to bstr, with at most 30
501
 * characters length. The bstr will be prepended by
502
 * a length byte.
503
 * NOTE: The name will must be already checked by
504
 *       affs_check_name()!
505
 */
506
 
507
int
508
affs_copy_name(unsigned char *bstr, struct dentry *dentry)
509
{
510
        int len = min(dentry->d_name.len, 30u);
511
 
512
        *bstr++ = len;
513
        memcpy(bstr, dentry->d_name.name, len);
514
        return len;
515
}

powered by: WebSVN 2.1.0

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