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

Subversion Repositories or1k

[/] [or1k/] [tags/] [LINUX_2_4_26_OR32/] [linux/] [linux-2.4/] [fs/] [affs/] [namei.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/*
2
 *  linux/fs/affs/namei.c
3
 *
4
 *  (c) 1996  Hans-Joachim Widmaier - Rewritten
5
 *
6
 *  (C) 1993  Ray Burr - Modified for Amiga FFS filesystem.
7
 *
8
 *  (C) 1991  Linus Torvalds - minix filesystem
9
 */
10
 
11
#include <linux/sched.h>
12
#include <linux/affs_fs.h>
13
#include <linux/kernel.h>
14
#include <linux/string.h>
15
#include <linux/stat.h>
16
#include <linux/fcntl.h>
17
#include <linux/locks.h>
18
#include <linux/amigaffs.h>
19
#include <asm/uaccess.h>
20
 
21
#include <linux/errno.h>
22
 
23
typedef int (*toupper_t)(int);
24
 
25
extern struct inode_operations affs_symlink_inode_operations;
26
 
27
static int       affs_toupper(int ch);
28
static int       affs_hash_dentry(struct dentry *, struct qstr *);
29
static int       affs_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
30
static int       affs_intl_toupper(int ch);
31
static int       affs_intl_hash_dentry(struct dentry *, struct qstr *);
32
static int       affs_intl_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
33
 
34
struct dentry_operations affs_dentry_operations = {
35
        d_hash:         affs_hash_dentry,
36
        d_compare:      affs_compare_dentry,
37
};
38
 
39
struct dentry_operations affs_intl_dentry_operations = {
40
        d_hash:         affs_intl_hash_dentry,
41
        d_compare:      affs_intl_compare_dentry,
42
};
43
 
44
 
45
/* Simple toupper() for DOS\1 */
46
 
47
static int
48
affs_toupper(int ch)
49
{
50
        return ch >= 'a' && ch <= 'z' ? ch -= ('a' - 'A') : ch;
51
}
52
 
53
/* International toupper() for DOS\3 ("international") */
54
 
55
static int
56
affs_intl_toupper(int ch)
57
{
58
        return (ch >= 'a' && ch <= 'z') || (ch >= 0xE0
59
                && ch <= 0xFE && ch != 0xF7) ?
60
                ch - ('a' - 'A') : ch;
61
}
62
 
63
static inline toupper_t
64
affs_get_toupper(struct super_block *sb)
65
{
66
        return AFFS_SB->s_flags & SF_INTL ? affs_intl_toupper : affs_toupper;
67
}
68
 
69
/*
70
 * Note: the dentry argument is the parent dentry.
71
 */
72
static inline int
73
__affs_hash_dentry(struct dentry *dentry, struct qstr *qstr, toupper_t toupper)
74
{
75
        const u8 *name = qstr->name;
76
        unsigned long hash;
77
        int i;
78
 
79
        i = affs_check_name(qstr->name,qstr->len);
80
        if (i)
81
                return i;
82
 
83
        hash = init_name_hash();
84
        i = min(qstr->len, 30u);
85
        for (; i > 0; name++, i--)
86
                hash = partial_name_hash(toupper(*name), hash);
87
        qstr->hash = end_name_hash(hash);
88
 
89
        return 0;
90
}
91
 
92
static int
93
affs_hash_dentry(struct dentry *dentry, struct qstr *qstr)
94
{
95
        return __affs_hash_dentry(dentry, qstr, affs_toupper);
96
}
97
static int
98
affs_intl_hash_dentry(struct dentry *dentry, struct qstr *qstr)
99
{
100
        return __affs_hash_dentry(dentry, qstr, affs_intl_toupper);
101
}
102
 
103
static inline int
104
__affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b, toupper_t toupper)
105
{
106
        const u8 *aname = a->name;
107
        const u8 *bname = b->name;
108
        int len;
109
 
110
        /* 'a' is the qstr of an already existing dentry, so the name
111
         * must be valid. 'b' must be validated first.
112
         */
113
 
114
        if (affs_check_name(b->name,b->len))
115
                return 1;
116
 
117
        /* If the names are longer than the allowed 30 chars,
118
         * the excess is ignored, so their length may differ.
119
         */
120
        len = a->len;
121
        if (len >= 30) {
122
                if (b->len < 30)
123
                        return 1;
124
                len = 30;
125
        } else if (len != b->len)
126
                return 1;
127
 
128
        for (; len > 0; len--)
129
                if (toupper(*aname++) != toupper(*bname++))
130
                        return 1;
131
 
132
        return 0;
133
}
134
 
135
static int
136
affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
137
{
138
        return __affs_compare_dentry(dentry, a, b, affs_toupper);
139
}
140
static int
141
affs_intl_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
142
{
143
        return __affs_compare_dentry(dentry, a, b, affs_intl_toupper);
144
}
145
 
146
/*
147
 * NOTE! unlike strncmp, affs_match returns 1 for success, 0 for failure.
148
 */
149
 
150
static inline int
151
affs_match(struct dentry *dentry, const u8 *name2, toupper_t toupper)
152
{
153
        const u8 *name = dentry->d_name.name;
154
        int len = dentry->d_name.len;
155
 
156
        if (len >= 30) {
157
                if (*name2 < 30)
158
                        return 0;
159
                len = 30;
160
        } else if (len != *name2)
161
                return 0;
162
 
163
        for (name2++; len > 0; len--)
164
                if (toupper(*name++) != toupper(*name2++))
165
                        return 0;
166
        return 1;
167
}
168
 
169
int
170
affs_hash_name(struct super_block *sb, const u8 *name, unsigned int len)
171
{
172
        toupper_t toupper = affs_get_toupper(sb);
173
        int hash;
174
 
175
        hash = len = min(len, 30u);
176
        for (; len > 0; len--)
177
                hash = (hash * 13 + toupper(*name++)) & 0x7ff;
178
 
179
        return hash % AFFS_SB->s_hashsize;
180
}
181
 
182
static struct buffer_head *
183
affs_find_entry(struct inode *dir, struct dentry *dentry)
184
{
185
        struct super_block *sb = dir->i_sb;
186
        struct buffer_head *bh;
187
        toupper_t toupper = affs_get_toupper(sb);
188
        u32 key;
189
 
190
        pr_debug("AFFS: find_entry(\"%.*s\")\n", (int)dentry->d_name.len, dentry->d_name.name);
191
 
192
        bh = affs_bread(sb, dir->i_ino);
193
        if (!bh)
194
                return ERR_PTR(-EIO);
195
 
196
        key = be32_to_cpu(AFFS_HEAD(bh)->table[affs_hash_name(sb, dentry->d_name.name, dentry->d_name.len)]);
197
 
198
        for (;;) {
199
                affs_brelse(bh);
200
                if (key == 0)
201
                        return NULL;
202
                bh = affs_bread(sb, key);
203
                if (!bh)
204
                        return ERR_PTR(-EIO);
205
                if (affs_match(dentry, AFFS_TAIL(sb, bh)->name, toupper))
206
                        return bh;
207
                key = be32_to_cpu(AFFS_TAIL(sb, bh)->hash_chain);
208
        }
209
}
210
 
211
struct dentry *
212
affs_lookup(struct inode *dir, struct dentry *dentry)
213
{
214
        struct super_block *sb = dir->i_sb;
215
        struct buffer_head *bh;
216
        struct inode *inode = NULL;
217
 
218
        pr_debug("AFFS: lookup(\"%.*s\")\n",(int)dentry->d_name.len,dentry->d_name.name);
219
 
220
        affs_lock_dir(dir);
221
        bh = affs_find_entry(dir, dentry);
222
        affs_unlock_dir(dir);
223
        if (IS_ERR(bh))
224
                return ERR_PTR(PTR_ERR(bh));
225
        if (bh) {
226
                u32 ino = bh->b_blocknr;
227
 
228
                /* store the real header ino in d_fsdata for faster lookups */
229
                dentry->d_fsdata = (void *)(long)ino;
230
                switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) {
231
                //link to dirs disabled
232
                //case ST_LINKDIR:
233
                case ST_LINKFILE:
234
                        ino = be32_to_cpu(AFFS_TAIL(sb, bh)->original);
235
                }
236
                affs_brelse(bh);
237
                inode = iget(sb, ino);
238
                if (!inode)
239
                        return ERR_PTR(-EACCES);
240
        }
241
        dentry->d_op = AFFS_SB->s_flags & SF_INTL ? &affs_intl_dentry_operations : &affs_dentry_operations;
242
        d_add(dentry, inode);
243
        return NULL;
244
}
245
 
246
int
247
affs_unlink(struct inode *dir, struct dentry *dentry)
248
{
249
        pr_debug("AFFS: unlink(dir=%d, \"%.*s\")\n", (u32)dir->i_ino,
250
                 (int)dentry->d_name.len, dentry->d_name.name);
251
 
252
        if (!dentry->d_inode)
253
                return -ENOENT;
254
 
255
        return affs_remove_header(dentry);
256
}
257
 
258
int
259
affs_create(struct inode *dir, struct dentry *dentry, int mode)
260
{
261
        struct super_block *sb = dir->i_sb;
262
        struct inode    *inode;
263
        int              error;
264
 
265
        pr_debug("AFFS: create(%lu,\"%.*s\",0%o)\n",dir->i_ino,(int)dentry->d_name.len,
266
                 dentry->d_name.name,mode);
267
 
268
        inode = affs_new_inode(dir);
269
        if (!inode)
270
                return -ENOSPC;
271
 
272
        inode->i_mode = mode;
273
        mode_to_prot(inode);
274
        mark_inode_dirty(inode);
275
 
276
        inode->i_op = &affs_file_inode_operations;
277
        inode->i_fop = &affs_file_operations;
278
        inode->i_mapping->a_ops = (AFFS_SB->s_flags & SF_OFS) ? &affs_aops_ofs : &affs_aops;
279
        error = affs_add_entry(dir, inode, dentry, ST_FILE);
280
        if (error) {
281
                inode->i_nlink = 0;
282
                iput(inode);
283
                return error;
284
        }
285
        return 0;
286
}
287
 
288
int
289
affs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
290
{
291
        struct inode            *inode;
292
        int                      error;
293
 
294
        pr_debug("AFFS: mkdir(%lu,\"%.*s\",0%o)\n",dir->i_ino,
295
                 (int)dentry->d_name.len,dentry->d_name.name,mode);
296
 
297
        inode = affs_new_inode(dir);
298
        if (!inode)
299
                return -ENOSPC;
300
 
301
        inode->i_mode = S_IFDIR | mode;
302
        mode_to_prot(inode);
303
 
304
        inode->i_op = &affs_dir_inode_operations;
305
        inode->i_fop = &affs_dir_operations;
306
 
307
        error = affs_add_entry(dir, inode, dentry, ST_USERDIR);
308
        if (error) {
309
                inode->i_nlink = 0;
310
                mark_inode_dirty(inode);
311
                iput(inode);
312
                return error;
313
        }
314
        return 0;
315
}
316
 
317
int
318
affs_rmdir(struct inode *dir, struct dentry *dentry)
319
{
320
        pr_debug("AFFS: rmdir(dir=%u, \"%.*s\")\n", (u32)dir->i_ino,
321
                 (int)dentry->d_name.len, dentry->d_name.name);
322
 
323
        if (!dentry->d_inode)
324
                return -ENOENT;
325
 
326
        return affs_remove_header(dentry);
327
}
328
 
329
int
330
affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
331
{
332
        struct super_block      *sb = dir->i_sb;
333
        struct buffer_head      *bh;
334
        struct inode            *inode;
335
        char                    *p;
336
        int                      i, maxlen, error;
337
        char                     c, lc;
338
 
339
        pr_debug("AFFS: symlink(%lu,\"%.*s\" -> \"%s\")\n",dir->i_ino,
340
                 (int)dentry->d_name.len,dentry->d_name.name,symname);
341
 
342
        maxlen = AFFS_SB->s_hashsize * sizeof(u32) - 1;
343
        error = -ENOSPC;
344
        inode  = affs_new_inode(dir);
345
        if (!inode)
346
                return -ENOSPC;
347
 
348
        inode->i_op = &affs_symlink_inode_operations;
349
        inode->i_data.a_ops = &affs_symlink_aops;
350
        inode->i_mode = S_IFLNK | 0777;
351
        mode_to_prot(inode);
352
 
353
        error = -EIO;
354
        bh = affs_bread(sb, inode->i_ino);
355
        if (!bh)
356
                goto err;
357
        i  = 0;
358
        p  = (char *)AFFS_HEAD(bh)->table;
359
        lc = '/';
360
        if (*symname == '/') {
361
                while (*symname == '/')
362
                        symname++;
363
                while (AFFS_SB->s_volume[i])    /* Cannot overflow */
364
                        *p++ = AFFS_SB->s_volume[i++];
365
        }
366
        while (i < maxlen && (c = *symname++)) {
367
                if (c == '.' && lc == '/' && *symname == '.' && symname[1] == '/') {
368
                        *p++ = '/';
369
                        i++;
370
                        symname += 2;
371
                        lc = '/';
372
                } else if (c == '.' && lc == '/' && *symname == '/') {
373
                        symname++;
374
                        lc = '/';
375
                } else {
376
                        *p++ = c;
377
                        lc   = c;
378
                        i++;
379
                }
380
                if (lc == '/')
381
                        while (*symname == '/')
382
                                symname++;
383
        }
384
        *p = 0;
385
        mark_buffer_dirty_inode(bh, inode);
386
        affs_brelse(bh);
387
        mark_inode_dirty(inode);
388
 
389
        error = affs_add_entry(dir, inode, dentry, ST_SOFTLINK);
390
        if (error)
391
                goto err;
392
 
393
        return 0;
394
 
395
err:
396
        inode->i_nlink = 0;
397
        mark_inode_dirty(inode);
398
        iput(inode);
399
        return error;
400
}
401
 
402
int
403
affs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
404
{
405
        struct inode *inode = old_dentry->d_inode;
406
        int error;
407
 
408
        pr_debug("AFFS: link(%u, %u, \"%.*s\")\n", (u32)inode->i_ino, (u32)dir->i_ino,
409
                 (int)dentry->d_name.len,dentry->d_name.name);
410
 
411
        if (S_ISDIR(inode->i_mode))
412
                return -EPERM;
413
 
414
        error = affs_add_entry(dir, inode, dentry, S_ISDIR(inode->i_mode) ? ST_LINKDIR : ST_LINKFILE);
415
        if (error) {
416
                inode->i_nlink = 0;
417
                mark_inode_dirty(inode);
418
                iput(inode);
419
                return error;
420
        }
421
        return 0;
422
}
423
 
424
int
425
affs_rename(struct inode *old_dir, struct dentry *old_dentry,
426
            struct inode *new_dir, struct dentry *new_dentry)
427
{
428
        struct super_block *sb = old_dir->i_sb;
429
        struct buffer_head *bh = NULL;
430
        int retval;
431
 
432
        pr_debug("AFFS: rename(old=%u,\"%*s\" to new=%u,\"%*s\")\n",
433
                 (u32)old_dir->i_ino, (int)old_dentry->d_name.len, old_dentry->d_name.name,
434
                 (u32)new_dir->i_ino, (int)new_dentry->d_name.len, new_dentry->d_name.name);
435
 
436
        if ((retval = affs_check_name(new_dentry->d_name.name,new_dentry->d_name.len)))
437
                goto done;
438
 
439
        /* Unlink destination if it already exists */
440
        if (new_dentry->d_inode) {
441
                retval = affs_remove_header(new_dentry);
442
                if (retval)
443
                        return retval;
444
        }
445
 
446
        retval = -EIO;
447
        bh = affs_bread(sb, old_dentry->d_inode->i_ino);
448
        if (!bh)
449
                goto done;
450
 
451
        /* Remove header from its parent directory. */
452
        affs_lock_dir(old_dir);
453
        retval = affs_remove_hash(old_dir, bh);
454
        affs_unlock_dir(old_dir);
455
        if (retval)
456
                goto done;
457
 
458
        /* And insert it into the new directory with the new name. */
459
        affs_copy_name(AFFS_TAIL(sb, bh)->name, new_dentry);
460
        affs_fix_checksum(sb, bh);
461
        affs_lock_dir(new_dir);
462
        retval = affs_insert_hash(new_dir, bh);
463
        affs_unlock_dir(new_dir);
464
        /* TODO: move it back to old_dir, if error? */
465
 
466
done:
467
        mark_buffer_dirty_inode(bh, retval ? old_dir : new_dir);
468
        affs_brelse(bh);
469
        return retval;
470
}

powered by: WebSVN 2.1.0

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