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

Subversion Repositories or1k

[/] [or1k/] [trunk/] [rc203soc/] [sw/] [uClinux/] [fs/] [msdos/] [namei.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1628 jcastillo
/*
2
 *  linux/fs/msdos/namei.c
3
 *
4
 *  Written 1992,1993 by Werner Almesberger
5
 *  Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu>
6
 */
7
 
8
#define __NO_VERSION__
9
#include <linux/module.h>
10
 
11
#include <linux/sched.h>
12
#include <linux/msdos_fs.h>
13
#include <linux/kernel.h>
14
#include <linux/errno.h>
15
#include <linux/string.h>
16
#include <linux/stat.h>
17
 
18
#include <asm/segment.h>
19
 
20
#include "../fat/msbuffer.h"
21
 
22
#define PRINTK(x)
23
 
24
 
25
/* MS-DOS "device special files" */
26
 
27
static const char *reserved_names[] = {
28
    "CON     ","PRN     ","NUL     ","AUX     ",
29
    "LPT1    ","LPT2    ","LPT3    ","LPT4    ",
30
    "COM1    ","COM2    ","COM3    ","COM4    ",
31
    NULL };
32
 
33
 
34
/* Characters that are undesirable in an MS-DOS file name */
35
 
36
static char bad_chars[] = "*?<>|\"";
37
static char bad_if_strict[] = "+=,; ";
38
 
39
 
40
void msdos_put_super(struct super_block *sb)
41
{
42
        fat_put_super(sb);
43
        MOD_DEC_USE_COUNT;
44
}
45
 
46
struct super_operations msdos_sops = {
47
        msdos_read_inode,
48
        fat_notify_change,
49
        fat_write_inode,
50
        fat_put_inode,
51
        msdos_put_super,
52
        NULL, /* added in 0.96c */
53
        fat_statfs,
54
        NULL
55
};
56
 
57
struct super_block *msdos_read_super(struct super_block *sb,void *data, int silent)
58
{
59
        struct super_block *res;
60
 
61
        MOD_INC_USE_COUNT;
62
 
63
        MSDOS_SB(sb)->options.isvfat = 0;
64
        sb->s_op = &msdos_sops;
65
        res =  fat_read_super(sb, data, silent);
66
        if (res == NULL)
67
                MOD_DEC_USE_COUNT;
68
 
69
        return res;
70
}
71
 
72
 
73
 
74
 
75
 
76
/***** Formats an MS-DOS file name. Rejects invalid names. */
77
static int msdos_format_name(char conv,const char *name,int len,
78
        char *res,int dot_dirs,char dotsOK)
79
        /* conv is relaxed/normal/strict, name is proposed name,
80
         * len is the length of the proposed name, res is the result name,
81
         * dot_dirs is . and .. are OK, dotsOK is if hidden files get dots.
82
         */
83
{
84
        char *walk;
85
        const char **reserved;
86
        unsigned char c;
87
        int space;
88
 
89
        if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) {
90
                if (!dot_dirs) return -EEXIST;
91
                memset(res+1,' ',10);
92
                while (len--) *res++ = '.';
93
                return 0;
94
        }
95
        if (name[0] == '.') {  /* dotfile because . and .. already done */
96
                if (!dotsOK) return -EINVAL;
97
                /* Get rid of dot - test for it elsewhere */
98
                name++; len--;
99
        }
100
        space = 1; /* disallow names that _really_ start with a dot */
101
        c = 0;
102
        for (walk = res; len && walk-res < 8; walk++) {
103
                c = *name++;
104
                len--;
105
                if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL;
106
                if (conv == 's' && strchr(bad_if_strict,c)) return -EINVAL;
107
                if (c >= 'A' && c <= 'Z' && conv == 's') return -EINVAL;
108
                if (c < ' ' || c == ':' || c == '\\') return -EINVAL;
109
/*  0xE5 is legal as a first character, but we must substitute 0x05     */
110
/*  because 0xE5 marks deleted files.  Yes, DOS really does this.       */
111
/*  It seems that Microsoft hacked DOS to support non-US characters     */
112
/*  after the 0xE5 character was already in use to mark deleted files.  */
113
                if((res==walk) && (c==0xE5)) c=0x05;
114
                if (c == '.') break;
115
                space = (c == ' ');
116
                *walk = (c >= 'a' && c <= 'z') ? c-32 : c;
117
        }
118
        if (space) return -EINVAL;
119
        if (conv == 's' && len && c != '.') {
120
                c = *name++;
121
                len--;
122
                if (c != '.') return -EINVAL;
123
        }
124
        while (c != '.' && len--) c = *name++;
125
        if (c == '.') {
126
                while (walk-res < 8) *walk++ = ' ';
127
                while (len > 0 && walk-res < MSDOS_NAME) {
128
                        c = *name++;
129
                        len--;
130
                        if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL;
131
                        if (conv == 's' && strchr(bad_if_strict,c))
132
                                return -EINVAL;
133
                        if (c < ' ' || c == ':' || c == '\\' || c == '.')
134
                                return -EINVAL;
135
                        if (c >= 'A' && c <= 'Z' && conv == 's') return -EINVAL;
136
                        space = c == ' ';
137
                        *walk++ = c >= 'a' && c <= 'z' ? c-32 : c;
138
                }
139
                if (space) return -EINVAL;
140
                if (conv == 's' && len) return -EINVAL;
141
        }
142
        while (walk-res < MSDOS_NAME) *walk++ = ' ';
143
        for (reserved = reserved_names; *reserved; reserved++)
144
                if (!strncmp(res,*reserved,8)) return -EINVAL;
145
        return 0;
146
}
147
 
148
 
149
/***** Locates a directory entry.  Uses unformatted name. */
150
static int msdos_find(struct inode *dir,const char *name,int len,
151
    struct buffer_head **bh,struct msdos_dir_entry **de,int *ino)
152
{
153
        char msdos_name[MSDOS_NAME];
154
        int res;
155
        char dotsOK;
156
        char scantype;
157
 
158
        dotsOK = MSDOS_SB(dir->i_sb)->options.dotsOK;
159
        res = msdos_format_name(MSDOS_SB(dir->i_sb)->options.name_check,
160
                                name,len, msdos_name,1,dotsOK);
161
        if (res < 0)
162
                return -ENOENT;
163
        if((name[0]=='.') && dotsOK){
164
            switch(len){
165
                case  0: panic("Empty name in msdos_find!");
166
                case  1: scantype = SCAN_ANY;                           break;
167
                case  2: scantype = ((name[1]=='.')?SCAN_ANY:SCAN_HID); break;
168
                default: scantype = SCAN_HID;
169
            }
170
        } else {
171
            scantype = (dotsOK ? SCAN_NOTHID : SCAN_ANY);
172
        }
173
        return fat_scan(dir,msdos_name,bh,de,ino,scantype);
174
}
175
 
176
/***** Get inode using directory and name */
177
int msdos_lookup(struct inode *dir,const char *name,int len,
178
    struct inode **result)
179
{
180
        struct super_block *sb = dir->i_sb;
181
        int ino,res;
182
        struct msdos_dir_entry *de;
183
        struct buffer_head *bh;
184
        struct inode *next;
185
 
186
        PRINTK (("msdos_lookup\n"));
187
 
188
        *result = NULL;
189
        if (!dir) return -ENOENT;
190
        if (!S_ISDIR(dir->i_mode)) {
191
                iput(dir);
192
                return -ENOENT;
193
        }
194
        PRINTK (("msdos_lookup 2\n"));
195
        if (len == 1 && name[0] == '.') {
196
                *result = dir;
197
                return 0;
198
        }
199
        if (len == 2 && name[0] == '.' && name[1] == '.') {
200
                ino = fat_parent_ino(dir,0);
201
                iput(dir);
202
                if (ino < 0) return ino;
203
                if (!(*result = iget(dir->i_sb,ino))) return -EACCES;
204
                return 0;
205
        }
206
#if 0
207
        if (dcache_lookup(dir, name, len, (unsigned long *) &ino)) {
208
                iput(dir);
209
                if (!(*result = iget(dir->i_sb, ino)))
210
                        return -EACCES;
211
                return 0;
212
        }
213
#endif
214
        PRINTK (("msdos_lookup 3\n"));
215
        if ((res = msdos_find(dir,name,len,&bh,&de,&ino)) < 0) {
216
                iput(dir);
217
                return res;
218
        }
219
        PRINTK (("msdos_lookup 4\n"));
220
        if (bh)
221
                fat_brelse(sb, bh);
222
        PRINTK (("msdos_lookup 4.5\n"));
223
        if (!(*result = iget(dir->i_sb,ino))) {
224
                iput(dir);
225
                return -EACCES;
226
        }
227
        PRINTK (("msdos_lookup 5\n"));
228
        if (!(*result)->i_sb ||
229
            ((*result)->i_sb->s_magic != MSDOS_SUPER_MAGIC)) {
230
                /* crossed a mount point into a non-msdos fs */
231
                iput(dir);
232
                return 0;
233
        }
234
        if (MSDOS_I(*result)->i_busy) { /* mkdir in progress */
235
                iput(*result);
236
                iput(dir);
237
                return -ENOENT;
238
        }
239
        PRINTK (("msdos_lookup 6\n"));
240
        while (MSDOS_I(*result)->i_old) {
241
                next = MSDOS_I(*result)->i_old;
242
                iput(*result);
243
                if (!(*result = iget(next->i_sb,next->i_ino))) {
244
                        fat_fs_panic(dir->i_sb,"msdos_lookup: Can't happen");
245
                        iput(dir);
246
                        return -ENOENT;
247
                }
248
        }
249
        PRINTK (("msdos_lookup 7\n"));
250
        iput(dir);
251
        PRINTK (("msdos_lookup 8\n"));
252
        return 0;
253
}
254
 
255
 
256
/***** Creates a directory entry (name is already formatted). */
257
static int msdos_create_entry(struct inode *dir, const char *name,int len,
258
    int is_dir, int is_hid, struct inode **result)
259
{
260
        struct super_block *sb = dir->i_sb;
261
        struct buffer_head *bh;
262
        struct msdos_dir_entry *de;
263
        int res,ino;
264
 
265
        if ((res = fat_scan(dir,NULL,&bh,&de,&ino,SCAN_ANY)) < 0) {
266
                if (res != -ENOENT) return res;
267
                if ((dir->i_ino == MSDOS_ROOT_INO) &&
268
                    (MSDOS_SB(sb)->fat_bits != 32))
269
                        return -ENOSPC;
270
                if ((res = fat_add_cluster(dir)) < 0) return res;
271
                if ((res = fat_scan(dir,NULL,&bh,&de,&ino,SCAN_ANY)) < 0) return res;
272
        }
273
        /*
274
         * XXX all times should be set by caller upon successful completion.
275
         */
276
        dir->i_ctime = dir->i_mtime = CURRENT_TIME;
277
        dir->i_dirt = 1;
278
        memcpy(de->name,name,MSDOS_NAME);
279
        de->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
280
        de->attr = is_hid ? (de->attr|ATTR_HIDDEN) : (de->attr&~ATTR_HIDDEN);
281
        de->start = 0;
282
        de->starthi = 0;
283
        fat_date_unix2dos(dir->i_mtime,&de->time,&de->date);
284
        de->size = 0;
285
        fat_mark_buffer_dirty(sb, bh, 1);
286
        if ((*result = iget(dir->i_sb,ino)) != NULL)
287
                msdos_read_inode(*result);
288
        fat_brelse(sb, bh);
289
        if (!*result) return -EIO;
290
        (*result)->i_mtime = (*result)->i_atime = (*result)->i_ctime =
291
            CURRENT_TIME;
292
        (*result)->i_dirt = 1;
293
        dcache_add(dir, name, len, ino);
294
        return 0;
295
}
296
 
297
/***** Create a file or directory */
298
int msdos_create(struct inode *dir,const char *name,int len,int mode,
299
        struct inode **result)
300
{
301
        struct super_block *sb = dir->i_sb;
302
        struct buffer_head *bh;
303
        struct msdos_dir_entry *de;
304
        char msdos_name[MSDOS_NAME];
305
        int ino,res,is_hid;
306
 
307
        if (!dir) return -ENOENT;
308
        if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->options.name_check,
309
                                     name,len,msdos_name,0,
310
                                     MSDOS_SB(dir->i_sb)->options.dotsOK)) < 0) {
311
                iput(dir);
312
                return res;
313
        }
314
        is_hid = (name[0]=='.') && (msdos_name[0]!='.');
315
        fat_lock_creation();
316
        /* Scan for existing file twice, so that creating a file fails
317
         * with -EINVAL if the other (dotfile/nondotfile) exists.
318
         * Else SCAN_ANY would do. Maybe use EACCES, EBUSY, ENOSPC, ENFILE?
319
         */
320
        if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_HID) >= 0) {
321
                fat_unlock_creation();
322
                fat_brelse(sb, bh);
323
                iput(dir);
324
                return is_hid ? -EEXIST : -EINVAL;
325
        }
326
        if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_NOTHID) >= 0) {
327
                fat_unlock_creation();
328
                fat_brelse(sb, bh);
329
                iput(dir);
330
                return is_hid ? -EINVAL : -EEXIST;
331
        }
332
        res = msdos_create_entry(dir,msdos_name,len,S_ISDIR(mode),is_hid,
333
                                 result);
334
        fat_unlock_creation();
335
        iput(dir);
336
        return res;
337
}
338
 
339
 
340
#ifdef DEBUG
341
 
342
static void dump_fat(struct super_block *sb,int start)
343
{
344
        printk("[");
345
        while (start) {
346
                printk("%d ",start);
347
                start = fat_access(sb,start,-1);
348
                if (!start) {
349
                        printk("ERROR");
350
                        break;
351
                }
352
                if (start == -1) break;
353
        }
354
        printk("]\n");
355
}
356
 
357
#endif
358
 
359
/***** See if directory is empty */
360
static int msdos_empty(struct inode *dir)
361
{
362
        struct super_block *sb = dir->i_sb;
363
        loff_t pos;
364
        struct buffer_head *bh;
365
        struct msdos_dir_entry *de;
366
 
367
        if (dir->i_count > 1)
368
                return -EBUSY;
369
        if (MSDOS_I(dir)->i_start) { /* may be zero in mkdir */
370
                pos = 0;
371
                bh = NULL;
372
                while (fat_get_entry(dir,&pos,&bh,&de) > -1) {
373
                        /* Ignore vfat longname entries */
374
                        if (de->attr == ATTR_EXT)
375
                                continue;
376
                        if (!IS_FREE(de->name) && strncmp(de->name,MSDOS_DOT,
377
                            MSDOS_NAME) && strncmp(de->name,MSDOS_DOTDOT,
378
                            MSDOS_NAME)) {
379
                                fat_brelse(sb, bh);
380
                                return -ENOTEMPTY;
381
                        }
382
                }
383
                if (bh)
384
                        fat_brelse(sb, bh);
385
        }
386
        return 0;
387
}
388
 
389
/***** Remove a directory */
390
int msdos_rmdir(struct inode *dir,const char *name,int len)
391
{
392
        struct super_block *sb = dir->i_sb;
393
        int res,ino;
394
        struct buffer_head *bh;
395
        struct msdos_dir_entry *de;
396
        struct inode *inode;
397
 
398
        bh = NULL;
399
        inode = NULL;
400
        res = -EPERM;
401
        if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.')))
402
                goto rmdir_done;
403
        if ((res = msdos_find(dir,name,len,&bh,&de,&ino)) < 0) goto rmdir_done;
404
        res = -ENOENT;
405
        if (!(inode = iget(dir->i_sb,ino))) goto rmdir_done;
406
        res = -ENOTDIR;
407
        if (!S_ISDIR(inode->i_mode)) goto rmdir_done;
408
        res = -EBUSY;
409
        if (dir->i_dev != inode->i_dev || dir == inode)
410
          goto rmdir_done;
411
        res = msdos_empty(inode);
412
        if (res)
413
                goto rmdir_done;
414
        inode->i_nlink = 0;
415
        inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
416
        dir->i_nlink--;
417
        inode->i_dirt = dir->i_dirt = 1;
418
        de->name[0] = DELETED_FLAG;
419
        fat_mark_buffer_dirty(sb, bh, 1);
420
        res = 0;
421
rmdir_done:
422
        fat_brelse(sb, bh);
423
        iput(dir);
424
        iput(inode);
425
        return res;
426
}
427
 
428
/***** Make a directory */
429
int msdos_mkdir(struct inode *dir,const char *name,int len,int mode)
430
{
431
        struct super_block *sb = dir->i_sb;
432
        struct buffer_head *bh;
433
        struct msdos_dir_entry *de;
434
        struct inode *inode,*dot;
435
        char msdos_name[MSDOS_NAME];
436
        int ino,res,is_hid;
437
 
438
        if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->options.name_check,
439
                                     name,len,msdos_name,0,
440
                                     MSDOS_SB(dir->i_sb)->options.dotsOK)) < 0) {
441
                iput(dir);
442
                return res;
443
        }
444
        is_hid = (name[0]=='.') && (msdos_name[0]!='.');
445
        fat_lock_creation();
446
        if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_ANY) >= 0) {
447
                fat_unlock_creation();
448
                fat_brelse(sb, bh);
449
                iput(dir);
450
                return -EEXIST;
451
        }
452
        if ((res = msdos_create_entry(dir,msdos_name,len,1,is_hid,
453
                                      &inode)) < 0) {
454
                fat_unlock_creation();
455
                iput(dir);
456
                return res;
457
        }
458
        dir->i_nlink++;
459
        inode->i_nlink = 2; /* no need to mark them dirty */
460
        MSDOS_I(inode)->i_busy = 1; /* prevent lookups */
461
        if ((res = fat_add_cluster(inode)) < 0) goto mkdir_error;
462
        if ((res = msdos_create_entry(inode,MSDOS_DOT,1,1,0,&dot)) < 0)
463
                goto mkdir_error;
464
        dot->i_size = inode->i_size; /* doesn't grow in the 2nd create_entry */
465
        MSDOS_I(dot)->i_start = MSDOS_I(inode)->i_start;
466
        MSDOS_I(dot)->i_logstart = MSDOS_I(inode)->i_logstart;
467
        dot->i_nlink = inode->i_nlink;
468
        dot->i_dirt = 1;
469
        iput(dot);
470
        if ((res = msdos_create_entry(inode,MSDOS_DOTDOT,2,1,0,&dot)) < 0)
471
                goto mkdir_error;
472
        fat_unlock_creation();
473
        dot->i_size = dir->i_size;
474
        MSDOS_I(dot)->i_start = MSDOS_I(dir)->i_start;
475
        MSDOS_I(dot)->i_logstart = MSDOS_I(dir)->i_logstart;
476
        dot->i_nlink = dir->i_nlink;
477
        dot->i_dirt = 1;
478
        MSDOS_I(inode)->i_busy = 0;
479
        iput(dot);
480
        iput(inode);
481
        iput(dir);
482
        return 0;
483
mkdir_error:
484
        iput(inode);
485
        if (msdos_rmdir(dir,name,len) < 0)
486
                fat_fs_panic(dir->i_sb,"rmdir in mkdir failed");
487
        fat_unlock_creation();
488
        return res;
489
}
490
 
491
/***** Unlink a file */
492
static int msdos_unlinkx(
493
        struct inode *dir,
494
        const char *name,
495
        int len,
496
        int nospc)      /* Flag special file ? */
497
{
498
        struct super_block *sb = dir->i_sb;
499
        int res,ino;
500
        struct buffer_head *bh;
501
        struct msdos_dir_entry *de;
502
        struct inode *inode;
503
 
504
        bh = NULL;
505
        inode = NULL;
506
        if ((res = msdos_find(dir,name,len,&bh,&de,&ino)) < 0)
507
                goto unlink_done;
508
        if (!(inode = iget(dir->i_sb,ino))) {
509
                res = -ENOENT;
510
                goto unlink_done;
511
        }
512
        if (!S_ISREG(inode->i_mode) && nospc){
513
                res = -EPERM;
514
                goto unlink_done;
515
        }
516
        if (IS_IMMUTABLE(inode)){
517
                res = -EPERM;
518
                goto unlink_done;
519
        }
520
        inode->i_nlink = 0;
521
        inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
522
        MSDOS_I(inode)->i_busy = 1;
523
        inode->i_dirt = dir->i_dirt = 1;
524
        de->name[0] = DELETED_FLAG;
525
        fat_mark_buffer_dirty(sb, bh, 1);
526
unlink_done:
527
        fat_brelse(sb, bh);
528
        iput(inode);
529
        iput(dir);
530
        return res;
531
}
532
 
533
/***** Unlink, as called for msdosfs */
534
int msdos_unlink(struct inode *dir,const char *name,int len)
535
{
536
        return msdos_unlinkx (dir,name,len,1);
537
}
538
 
539
/***** Unlink, as called for umsdosfs */
540
int msdos_unlink_umsdos(struct inode *dir,const char *name,int len)
541
{
542
        return msdos_unlinkx (dir,name,len,0);
543
}
544
 
545
/***** Rename within a directory */
546
static int rename_same_dir(struct inode *old_dir,char *old_name,int old_len,
547
    struct inode *new_dir,char *new_name,int new_len,
548
    struct buffer_head *old_bh,
549
    struct msdos_dir_entry *old_de,int old_ino,int is_hid)
550
{
551
        struct super_block *sb = old_dir->i_sb;
552
        struct buffer_head *new_bh;
553
        struct msdos_dir_entry *new_de;
554
        struct inode *new_inode,*old_inode;
555
        int new_ino,exists,error;
556
 
557
        if (!strncmp(old_name,new_name,MSDOS_NAME)) goto set_hid;
558
        exists = fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino,SCAN_ANY) >= 0;
559
        if (*(unsigned char *) old_de->name == DELETED_FLAG) {
560
                if (exists)
561
                        fat_brelse(sb, new_bh);
562
                return -ENOENT;
563
        }
564
        if (exists) {
565
                if (!(new_inode = iget(new_dir->i_sb,new_ino))) {
566
                        fat_brelse(sb, new_bh);
567
                        return -EIO;
568
                }
569
                error = S_ISDIR(new_inode->i_mode)
570
                        ? (old_de->attr & ATTR_DIR)
571
                                ? msdos_empty(new_inode)
572
                                : -EPERM
573
                        : (old_de->attr & ATTR_DIR)
574
                                ? -EPERM
575
                                : 0;
576
                if (!error && (old_de->attr & ATTR_SYS)) error = -EPERM;
577
                if (error) {
578
                        iput(new_inode);
579
                        fat_brelse(sb, new_bh);
580
                        return error;
581
                }
582
                if (S_ISDIR(new_inode->i_mode)) {
583
                        new_dir->i_nlink--;
584
                        new_dir->i_dirt = 1;
585
                }
586
                new_inode->i_nlink = 0;
587
                MSDOS_I(new_inode)->i_busy = 1;
588
                new_inode->i_dirt = 1;
589
                new_de->name[0] = DELETED_FLAG;
590
                fat_mark_buffer_dirty(sb, new_bh, 1);
591
                dcache_add(new_dir, new_name, new_len, new_ino);
592
                iput(new_inode);
593
                fat_brelse(sb, new_bh);
594
        }
595
        memcpy(old_de->name,new_name,MSDOS_NAME);
596
set_hid:
597
        old_de->attr = is_hid
598
                ? (old_de->attr | ATTR_HIDDEN)
599
                : (old_de->attr &~ ATTR_HIDDEN);
600
        fat_mark_buffer_dirty(sb, old_bh, 1);
601
        /* update binary info for conversion, i_attrs */
602
        if ((old_inode = iget(old_dir->i_sb,old_ino)) != NULL) {
603
                MSDOS_I(old_inode)->i_attrs = is_hid
604
                        ? (MSDOS_I(old_inode)->i_attrs |  ATTR_HIDDEN)
605
                        : (MSDOS_I(old_inode)->i_attrs &~ ATTR_HIDDEN);
606
                iput(old_inode);
607
        }
608
        return 0;
609
}
610
 
611
/***** Rename across directories - a nonphysical move */
612
static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len,
613
    struct inode *new_dir,char *new_name,int new_len,
614
    struct buffer_head *old_bh,
615
    struct msdos_dir_entry *old_de,int old_ino,int is_hid)
616
{
617
        struct super_block *sb = old_dir->i_sb;
618
        struct buffer_head *new_bh,*free_bh,*dotdot_bh;
619
        struct msdos_dir_entry *new_de,*free_de,*dotdot_de;
620
        struct inode *old_inode,*new_inode,*free_inode,*dotdot_inode,*walk;
621
        int new_ino,free_ino,dotdot_ino;
622
        int error,exists,ino;
623
 
624
        if (old_dir->i_dev != new_dir->i_dev) return -EINVAL;
625
        if (old_ino == new_dir->i_ino) return -EINVAL;
626
        if (!(walk = iget(new_dir->i_sb,new_dir->i_ino))) return -EIO;
627
        /* prevent moving directory below itself */
628
        while (walk->i_ino != MSDOS_ROOT_INO) {
629
                ino = fat_parent_ino(walk,1);
630
                iput(walk);
631
                if (ino < 0) return ino;
632
                if (ino == old_ino) return -EINVAL;
633
                if (!(walk = iget(new_dir->i_sb,ino))) return -EIO;
634
        }
635
        iput(walk);
636
        /* find free spot */
637
        while ((error = fat_scan(new_dir,NULL,&free_bh,&free_de,&free_ino,
638
            SCAN_ANY)) < 0) {
639
                if (error != -ENOENT) return error;
640
                error = fat_add_cluster(new_dir);
641
                if (error) return error;
642
        }
643
        exists = fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino,SCAN_ANY) >= 0;
644
        if (!(old_inode = iget(old_dir->i_sb,old_ino))) {
645
                fat_brelse(sb, free_bh);
646
                if (exists)
647
                        fat_brelse(sb, new_bh);
648
                return -EIO;
649
        }
650
        if (*(unsigned char *) old_de->name == DELETED_FLAG) {
651
                iput(old_inode);
652
                fat_brelse(sb, free_bh);
653
                if (exists)
654
                        fat_brelse(sb, new_bh);
655
                return -ENOENT;
656
        }
657
        new_inode = NULL; /* to make GCC happy */
658
        if (exists) {  /* Trash the old file! */
659
                if (!(new_inode = iget(new_dir->i_sb,new_ino))) {
660
                        iput(old_inode);
661
                        fat_brelse(sb, new_bh);
662
                        return -EIO;
663
                }
664
                error = S_ISDIR(new_inode->i_mode)
665
                        ? (old_de->attr & ATTR_DIR)
666
                                ? msdos_empty(new_inode)
667
                                : -EPERM
668
                        : (old_de->attr & ATTR_DIR)
669
                                ? -EPERM
670
                                : 0;
671
                if (!error && (old_de->attr & ATTR_SYS)) error = -EPERM;
672
                if (error) {
673
                        iput(new_inode);
674
                        iput(old_inode);
675
                        fat_brelse(sb, new_bh);
676
                        return error;
677
                }
678
                new_inode->i_nlink = 0;
679
                MSDOS_I(new_inode)->i_busy = 1;
680
                new_inode->i_dirt = 1;
681
                new_de->name[0] = DELETED_FLAG;
682
                fat_mark_buffer_dirty(sb, new_bh, 1);
683
        }
684
        memcpy(free_de,old_de,sizeof(struct msdos_dir_entry));
685
        memcpy(free_de->name,new_name,MSDOS_NAME);
686
        free_de->attr = is_hid
687
                ? (free_de->attr|ATTR_HIDDEN)
688
                : (free_de->attr&~ATTR_HIDDEN);
689
        if (!(free_inode = iget(new_dir->i_sb,free_ino))) {
690
                free_de->name[0] = DELETED_FLAG;
691
/*  Don't mark free_bh as dirty. Both states are supposed to be equivalent. */
692
                fat_brelse(sb, free_bh);
693
                if (exists) {
694
                        iput(new_inode);
695
                        fat_brelse(sb, new_bh);
696
                }
697
                return -EIO;
698
        }
699
        if (exists && S_ISDIR(new_inode->i_mode)) {
700
                new_dir->i_nlink--;
701
                new_dir->i_dirt = 1;
702
        }
703
        msdos_read_inode(free_inode);
704
        MSDOS_I(old_inode)->i_busy = 1;
705
        MSDOS_I(old_inode)->i_linked = free_inode;
706
        MSDOS_I(free_inode)->i_oldlink = old_inode;
707
        fat_cache_inval_inode(old_inode);
708
        old_inode->i_dirt = 1;
709
        old_de->name[0] = DELETED_FLAG;
710
        fat_mark_buffer_dirty(sb, old_bh, 1);
711
        fat_mark_buffer_dirty(sb, free_bh, 1);
712
        if (exists) {
713
                MSDOS_I(new_inode)->i_depend = free_inode;
714
                MSDOS_I(free_inode)->i_old = new_inode;
715
                /* Two references now exist to free_inode so increase count */
716
                free_inode->i_count++;
717
                /* free_inode is put after putting new_inode and old_inode */
718
                iput(new_inode);
719
                dcache_add(new_dir, new_name, new_len, new_ino);
720
                fat_brelse(sb, new_bh);
721
        }
722
        if (S_ISDIR(old_inode->i_mode)) {
723
                if ((error = fat_scan(old_inode,MSDOS_DOTDOT,&dotdot_bh,
724
                    &dotdot_de,&dotdot_ino,SCAN_ANY)) < 0) goto rename_done;
725
                if (!(dotdot_inode = iget(old_inode->i_sb,dotdot_ino))) {
726
                        fat_brelse(sb, dotdot_bh);
727
                        error = -EIO;
728
                        goto rename_done;
729
                }
730
                MSDOS_I(dotdot_inode)->i_start = MSDOS_I(new_dir)->i_start;
731
                MSDOS_I(dotdot_inode)->i_logstart = MSDOS_I(new_dir)->i_logstart;
732
                dotdot_de->start = CT_LE_W(MSDOS_I(new_dir)->i_logstart);
733
                dotdot_de->starthi = CT_LE_W((MSDOS_I(new_dir)->i_logstart) >> 16);
734
                dotdot_inode->i_dirt = 1;
735
                fat_mark_buffer_dirty(sb, dotdot_bh, 1);
736
                old_dir->i_nlink--;
737
                new_dir->i_nlink++;
738
                /* no need to mark them dirty */
739
                dotdot_inode->i_nlink = new_dir->i_nlink;
740
                iput(dotdot_inode);
741
                fat_brelse(sb, dotdot_bh);
742
        }
743
        error = 0;
744
rename_done:
745
        fat_brelse(sb, free_bh);
746
        iput(old_inode);
747
        return error;
748
}
749
 
750
/***** Rename, a wrapper for rename_same_dir & rename_diff_dir */
751
int msdos_rename(struct inode *old_dir,const char *old_name,int old_len,
752
        struct inode *new_dir,const char *new_name,int new_len,
753
        int must_be_dir)
754
{
755
        struct super_block *sb = old_dir->i_sb;
756
        char old_msdos_name[MSDOS_NAME],new_msdos_name[MSDOS_NAME];
757
        struct buffer_head *old_bh;
758
        struct msdos_dir_entry *old_de;
759
        int old_ino,error;
760
        int is_hid,old_hid; /* if new file and old file are hidden */
761
 
762
        if ((error = msdos_format_name(MSDOS_SB(old_dir->i_sb)->options.name_check,
763
                                       old_name,old_len,old_msdos_name,1,
764
                                       MSDOS_SB(old_dir->i_sb)->options.dotsOK))
765
            < 0) goto rename_done;
766
        if ((error = msdos_format_name(MSDOS_SB(new_dir->i_sb)->options.name_check,
767
                                       new_name,new_len,new_msdos_name,0,
768
                                       MSDOS_SB(new_dir->i_sb)->options.dotsOK))
769
            < 0) goto rename_done;
770
        is_hid = (new_name[0]=='.') && (new_msdos_name[0]!='.');
771
        old_hid = (old_name[0]=='.') && (old_msdos_name[0]!='.');
772
        if ((error = fat_scan(old_dir,old_msdos_name,&old_bh,&old_de,
773
            &old_ino,old_hid?SCAN_HID:SCAN_NOTHID)) < 0) goto rename_done;
774
        fat_lock_creation();
775
        if (old_dir == new_dir)
776
                error = rename_same_dir(old_dir,old_msdos_name,old_len,new_dir,
777
                    new_msdos_name,new_len,old_bh,old_de,old_ino,is_hid);
778
        else error = rename_diff_dir(old_dir,old_msdos_name,old_len,new_dir,
779
                    new_msdos_name,new_len,old_bh,old_de,old_ino,is_hid);
780
        fat_unlock_creation();
781
        fat_brelse(sb, old_bh);
782
rename_done:
783
        iput(old_dir);
784
        iput(new_dir);
785
        return error;
786
}
787
 
788
 
789
/* The public inode operations for the msdos fs */
790
struct inode_operations msdos_dir_inode_operations = {
791
        &fat_dir_operations,    /* default directory file-ops */
792
        msdos_create,           /* create */
793
        msdos_lookup,           /* lookup */
794
        NULL,                   /* link */
795
        msdos_unlink,           /* unlink */
796
        NULL,                   /* symlink */
797
        msdos_mkdir,            /* mkdir */
798
        msdos_rmdir,            /* rmdir */
799
        NULL,                   /* mknod */
800
        msdos_rename,           /* rename */
801
        NULL,                   /* readlink */
802
        NULL,                   /* follow_link */
803
        NULL,                   /* readpage */
804
        NULL,                   /* writepage */
805
        fat_bmap,               /* bmap */
806
        NULL,                   /* truncate */
807
        NULL                    /* permission */
808
};
809
 
810
 
811
void msdos_read_inode(struct inode *inode)
812
{
813
        fat_read_inode(inode, &msdos_dir_inode_operations);
814
}
815
 
816
 
817
 
818
#ifdef MODULE
819
int init_module(void)
820
{
821
        return init_msdos_fs();
822
}
823
 
824
 
825
void cleanup_module(void)
826
{
827
        unregister_filesystem(&msdos_fs_type);
828
}
829
 
830
#endif
831
 

powered by: WebSVN 2.1.0

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