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

Subversion Repositories or1k

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

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1628 jcastillo
/*
2
 *  linux/fs/vfat/namei.c
3
 *
4
 *  Written 1992,1993 by Werner Almesberger
5
 *
6
 *  Windows95/Windows NT compatible extended MSDOS filesystem
7
 *    by Gordon Chaffee Copyright (C) 1995.  Send bug reports for the
8
 *    VFAT filesystem to <chaffee@plateau.cs.berkeley.edu>.  Specify
9
 *    what file operation caused you trouble and if you can duplicate
10
 *    the problem, send a script that demonstrates it.
11
 */
12
 
13
#define __NO_VERSION__
14
#include <linux/module.h>
15
 
16
#include <linux/sched.h>
17
#include <linux/msdos_fs.h>
18
#include <linux/nls.h>
19
#include <linux/kernel.h>
20
#include <linux/errno.h>
21
#include <linux/string.h>
22
#include <linux/stat.h>
23
#include <linux/mm.h>
24
#include <linux/malloc.h>
25
 
26
#include "../fat/msbuffer.h"
27
 
28
#if 0
29
# define PRINTK(x) printk x
30
#else
31
# define PRINTK(x)
32
#endif
33
 
34
#ifndef DEBUG
35
# define CHECK_STACK
36
#else
37
# define CHECK_STACK check_stack(__FILE__, __LINE__)
38
#endif
39
 
40
/*
41
 * XXX: It would be better to use the tolower from linux/ctype.h,
42
 * but _ctype is needed and it is not exported.
43
 */
44
#define tolower(c) (((c) >= 'A' && (c) <= 'Z') ? (c)-('A'-'a') : (c))
45
 
46
struct vfat_find_info {
47
        const char *name;
48
        int len;
49
        int new_filename;
50
        int found;
51
        int is_long;
52
        off_t offset;
53
        off_t short_offset;
54
        int long_slots;
55
        ino_t ino;
56
        int posix;
57
};
58
 
59
void vfat_read_inode(struct inode *inode);
60
 
61
void vfat_put_super(struct super_block *sb)
62
{
63
        fat_put_super(sb);
64
        MOD_DEC_USE_COUNT;
65
}
66
 
67
 
68
static struct super_operations vfat_sops = {
69
        vfat_read_inode,
70
        fat_notify_change,
71
        fat_write_inode,
72
        fat_put_inode,
73
        vfat_put_super,
74
        NULL, /* added in 0.96c */
75
        fat_statfs,
76
        NULL
77
};
78
 
79
static int parse_options(char *options, struct fat_mount_options *opts)
80
{
81
        char *this_char,*value,save,*savep;
82
        int ret;
83
 
84
        opts->unicode_xlate = opts->posixfs = 0;
85
        opts->numtail = 1;
86
        opts->utf8 = 0;
87
 
88
        if (!options) return 1;
89
        save = 0;
90
        savep = NULL;
91
        ret = 1;
92
        for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) {
93
                if ((value = strchr(this_char,'=')) != NULL) {
94
                        save = *value;
95
                        savep = value;
96
                        *value++ = 0;
97
                }
98
                if (!strcmp(this_char,"utf8")) {
99
                        if (value) {
100
                                ret = 0;
101
                        } else {
102
                                opts->utf8 = 1;
103
                        }
104
                } else if (!strcmp(this_char,"uni_xlate")) {
105
                        if (value) {
106
                                ret = 0;
107
                        } else {
108
                                opts->unicode_xlate = 1;
109
                        }
110
                }
111
                else if (!strcmp(this_char,"posix")) {
112
                        if (value) {
113
                                ret = 0;
114
                        } else {
115
                                opts->posixfs = 1;
116
                        }
117
                }
118
                else if (!strcmp(this_char,"nonumtail")) {
119
                        if (value) {
120
                                ret = 0;
121
                        } else {
122
                                opts->numtail = 0;
123
                        }
124
                }
125
                if (this_char != options)
126
                        *(this_char-1) = ',';
127
                if (value) {
128
                        *savep = save;
129
                }
130
                if (ret == 0) {
131
                        return 0;
132
                }
133
        }
134
        if (opts->unicode_xlate) {
135
                opts->utf8 = 0;
136
        }
137
        return 1;
138
}
139
 
140
struct super_block *vfat_read_super(struct super_block *sb,void *data,
141
                                    int silent)
142
{
143
        struct super_block *res;
144
 
145
        MOD_INC_USE_COUNT;
146
 
147
        MSDOS_SB(sb)->options.isvfat = 1;
148
 
149
        sb->s_op = &vfat_sops;
150
        res = fat_read_super(sb, data, silent);
151
        if (res == NULL) {
152
                sb->s_dev = 0;
153
                MOD_DEC_USE_COUNT;
154
                return NULL;
155
        }
156
 
157
        if (!parse_options((char *) data, &(MSDOS_SB(sb)->options))) {
158
                MOD_DEC_USE_COUNT;
159
        } else {
160
                MSDOS_SB(sb)->options.dotsOK = 0;
161
        }
162
 
163
        return res;
164
}
165
 
166
#ifdef DEBUG
167
 
168
static void
169
check_stack(const char *fname, int lineno)
170
{
171
        int stack_level;
172
        char *pg_dir;
173
 
174
        stack_level = (long)(&pg_dir)-current->kernel_stack_page;
175
        if (stack_level < 0)
176
                printk("*-*-*-* vfat kstack overflow in %s line %d: SL=%d\n",
177
                       fname, lineno, stack_level);
178
        else if (stack_level < 500)
179
                printk("*-*-*-* vfat kstack low in %s line %d: SL=%d\n",
180
                       fname, lineno, stack_level);
181
#if 0
182
        else
183
                printk("------- vfat kstack ok in %s line %d: SL=%d\n",
184
                       fname, lineno, stack_level);
185
#endif
186
        if (*(unsigned long *) current->kernel_stack_page != STACK_MAGIC) {
187
                printk("******* vfat stack corruption detected in %s at line %d\n",
188
                       fname, lineno);
189
        }
190
}
191
 
192
static int debug = 0;
193
static void dump_fat(struct super_block *sb,int start)
194
{
195
        printk("[");
196
        while (start) {
197
                printk("%d ",start);
198
                start = fat_access(sb,start,-1);
199
                if (!start) {
200
                        printk("ERROR");
201
                        break;
202
                }
203
                if (start == -1) break;
204
        }
205
        printk("]\n");
206
}
207
 
208
static void dump_de(struct msdos_dir_entry *de)
209
{
210
        int i;
211
        unsigned char *p = (unsigned char *) de;
212
        printk("[");
213
 
214
        for (i = 0; i < 32; i++, p++) {
215
                printk("%02x ", *p);
216
        }
217
        printk("]\n");
218
}
219
 
220
#endif
221
 
222
/* MS-DOS "device special files" */
223
 
224
static const char *reserved_names[] = {
225
        "CON     ","PRN     ","NUL     ","AUX     ",
226
        "LPT1    ","LPT2    ","LPT3    ","LPT4    ",
227
        "COM1    ","COM2    ","COM3    ","COM4    ",
228
        NULL };
229
 
230
 
231
/* Characters that are undesirable in an MS-DOS file name */
232
 
233
static char bad_chars[] = "*?<>|\":/\\";
234
static char replace_chars[] = "[];,+=";
235
 
236
static int vfat_find(struct inode *dir,const char *name,int len,
237
                      int find_long,int new_filename,int is_dir,
238
                      struct slot_info *sinfo_out);
239
 
240
/* Checks the validity of an long MS-DOS filename */
241
/* Returns negative number on error, 0 for a normal
242
 * return, and 1 for . or .. */
243
 
244
static int vfat_valid_longname(const char *name, int len, int dot_dirs,
245
                               int xlate)
246
{
247
        const char **reserved;
248
        unsigned char c;
249
        int i;
250
 
251
        if (IS_FREE(name)) return -EINVAL;
252
        if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) {
253
                if (!dot_dirs) return -EEXIST;
254
                return 1;
255
        }
256
 
257
        if (len && name[len-1] == ' ') return -EINVAL;
258
        if (len >= 256) return -EINVAL;
259
        for (i = 0; i < len; i++) {
260
                c = name[i];
261
                if (xlate && c == ':') continue;
262
                if (strchr(bad_chars,c)) {
263
                        return -EINVAL;
264
                }
265
        }
266
        if (len == 3 || len == 4) {
267
                for (reserved = reserved_names; *reserved; reserved++)
268
                        if (!strncmp(name,*reserved,8)) return -EINVAL;
269
        }
270
        return 0;
271
}
272
 
273
static int vfat_valid_shortname(const char *name,int len,
274
                                int dot_dirs, int utf8)
275
{
276
        const char *walk, **reserved;
277
        unsigned char c;
278
        int space;
279
 
280
        if (IS_FREE(name)) return -EINVAL;
281
        if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) {
282
                if (!dot_dirs) return -EEXIST;
283
                return 1;
284
        }
285
 
286
        space = 1; /* disallow names starting with a dot */
287
        c = 0;
288
        for (walk = name; len && walk-name < 8;) {
289
                c = *walk++;
290
                len--;
291
                if (utf8 && (c & 0x80)) return -EINVAL;
292
                if (strchr(bad_chars,c)) return -EINVAL;
293
                if (strchr(replace_chars,c)) return -EINVAL;
294
                if (c >= 'A' && c <= 'Z') return -EINVAL;
295
                if (c < ' ' || c == ':' || c == '\\') return -EINVAL;
296
                if ((walk == name) && (c == 0xE5)) c = 0x05;
297
                if (c == '.') break;
298
                space = c == ' ';
299
        }
300
        if (space) return -EINVAL;
301
        if (len && c != '.') {
302
                c = *walk++;
303
                len--;
304
                if (c != '.') return -EINVAL;
305
        }
306
        while (c != '.' && len--) c = *walk++;
307
        if (c == '.') {
308
                if (len >= 4) return -EINVAL;
309
                while (len > 0 && walk-name < (MSDOS_NAME+1)) {
310
                        c = *walk++;
311
                        len--;
312
                        if (utf8 && (c & 0x80)) return -EINVAL;
313
                        if (strchr(bad_chars,c)) return -EINVAL;
314
                        if (strchr(replace_chars,c))
315
                                return -EINVAL;
316
                        if (c < ' ' || c == ':' || c == '\\' || c == '.')
317
                                return -EINVAL;
318
                        if (c >= 'A' && c <= 'Z') return -EINVAL;
319
                        space = c == ' ';
320
                }
321
                if (space) return -EINVAL;
322
                if (len) return -EINVAL;
323
        }
324
        for (reserved = reserved_names; *reserved; reserved++)
325
                if (!strncmp(name,*reserved,8)) return -EINVAL;
326
 
327
        return 0;
328
}
329
 
330
/* Takes a short filename and converts it to a formatted MS-DOS filename.
331
 * If the short filename is not a valid MS-DOS filename, an error is
332
 * returned.  The formatted short filename is returned in 'res'.
333
 */
334
 
335
static int vfat_format_name(const char *name,int len,char *res,
336
  int dot_dirs,int utf8)
337
{
338
        char *walk;
339
        const char **reserved;
340
        unsigned char c;
341
        int space;
342
 
343
        if (IS_FREE(name)) return -EINVAL;
344
        if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) {
345
                if (!dot_dirs) return -EEXIST;
346
                memset(res+1,' ',10);
347
                while (len--) *res++ = '.';
348
                return 0;
349
        }
350
 
351
        space = 1; /* disallow names starting with a dot */
352
        c = 0;
353
        for (walk = res; len && walk-res < 8; walk++) {
354
                c = *name++;
355
                len--;
356
                if (utf8 && (c & 0x80)) return -EINVAL;
357
                if (strchr(bad_chars,c)) return -EINVAL;
358
                if (strchr(replace_chars,c)) return -EINVAL;
359
                if (c >= 'A' && c <= 'Z') return -EINVAL;
360
                if (c < ' ' || c == ':' || c == '\\') return -EINVAL;
361
                if (c == '.') break;
362
                space = c == ' ';
363
                *walk = c >= 'a' && c <= 'z' ? c-32 : c;
364
        }
365
        if (space) return -EINVAL;
366
        if (len && c != '.') {
367
                c = *name++;
368
                len--;
369
                if (c != '.') return -EINVAL;
370
        }
371
        while (c != '.' && len--) c = *name++;
372
        if (c == '.') {
373
                while (walk-res < 8) *walk++ = ' ';
374
                while (len > 0 && walk-res < MSDOS_NAME) {
375
                        c = *name++;
376
                        len--;
377
                        if (utf8 && (c & 0x80)) return -EINVAL;
378
                        if (strchr(bad_chars,c)) return -EINVAL;
379
                        if (strchr(replace_chars,c))
380
                                return -EINVAL;
381
                        if (c < ' ' || c == ':' || c == '\\' || c == '.')
382
                                return -EINVAL;
383
                        if (c >= 'A' && c <= 'Z') return -EINVAL;
384
                        space = c == ' ';
385
                        *walk++ = c >= 'a' && c <= 'z' ? c-32 : c;
386
                }
387
                if (space) return -EINVAL;
388
                if (len) return -EINVAL;
389
        }
390
        while (walk-res < MSDOS_NAME) *walk++ = ' ';
391
        for (reserved = reserved_names; *reserved; reserved++)
392
                if (!strncmp(res,*reserved,8)) return -EINVAL;
393
 
394
        return 0;
395
}
396
 
397
static char skip_chars[] = ".:\"?<>| ";
398
 
399
/* Given a valid longname, create a unique shortname.  Make sure the
400
 * shortname does not exist
401
 */
402
static int vfat_create_shortname(struct inode *dir, const char *name,
403
     int len, char *name_res, int utf8)
404
{
405
        const char *ip, *ext_start, *end;
406
        char *p;
407
        int sz, extlen, baselen, totlen;
408
        char msdos_name[13];
409
        char base[9], ext[4];
410
        int i;
411
        int res;
412
        int spaces;
413
        char buf[8];
414
        struct slot_info sinfo;
415
        const char *name_start;
416
 
417
        PRINTK(("Entering vfat_create_shortname: name=%s, len=%d\n", name, len));
418
        sz = 0;                  /* Make compiler happy */
419
        if (len && name[len-1]==' ') return -EINVAL;
420
        if (len <= 12) {
421
                /* Do a case insensitive search if the name would be a valid
422
                 * shortname if is were all capitalized.  However, do not
423
                 * allow spaces in short names because Win95 scandisk does
424
                 * not like that */
425
                res = 0;
426
                for (i = 0, p = msdos_name, ip = name; i < len; i++, p++, ip++)
427
                {
428
                        if (*ip == ' ') {
429
                                res = -EINVAL;
430
                                break;
431
                        }
432
                        if (*ip >= 'A' && *ip <= 'Z') {
433
                                *p = *ip + 32;
434
                        } else {
435
                                *p = *ip;
436
                        }
437
                }
438
                if (res == 0) {
439
                        res = vfat_format_name(msdos_name, len, name_res, 1, utf8);
440
                }
441
                if (res > -1) {
442
                        PRINTK(("vfat_create_shortname 1\n"));
443
                        res = vfat_find(dir, msdos_name, len, 0, 0, 0, &sinfo);
444
                        PRINTK(("vfat_create_shortname 2\n"));
445
                        if (res > -1) return -EEXIST;
446
                        return 0;
447
                }
448
        }
449
 
450
        PRINTK(("vfat_create_shortname 3\n"));
451
        /* Now, we need to create a shortname from the long name */
452
        ext_start = end = &name[len];
453
        while (--ext_start >= name) {
454
                if (*ext_start == '.') {
455
                        if (ext_start == end - 1) {
456
                                sz = len;
457
                                ext_start = NULL;
458
                        }
459
                        break;
460
                }
461
        }
462
        if (ext_start == name - 1) {
463
                sz = len;
464
                ext_start = NULL;
465
        } else if (ext_start) {
466
                /*
467
                 * Names which start with a dot could be just
468
                 * an extension eg. "...test".  In this case Win95
469
                 * uses the extension as the name and sets no extension.
470
                 */
471
                name_start = &name[0];
472
                while (name_start < ext_start)
473
                {
474
                        if (!strchr(skip_chars,*name_start)) break;
475
                        name_start++;
476
                }
477
                if (name_start != ext_start) {
478
                        sz = ext_start - name;
479
                        ext_start++;
480
                } else {
481
                        sz = len;
482
                        ext_start=NULL;
483
                }
484
        }
485
 
486
        for (baselen = i = 0, p = base, ip = name; i < sz && baselen < 8; i++)
487
        {
488
                if (utf8 && (*ip & 0x80)) {
489
                        *p++ = '_';
490
                        baselen++;
491
                } else if (!strchr(skip_chars, *ip)) {
492
                        if (*ip >= 'A' && *ip <= 'Z') {
493
                                *p = *ip + 32;
494
                        } else {
495
                                *p = *ip;
496
                        }
497
                        if (strchr(replace_chars, *p)) *p='_';
498
                        p++; baselen++;
499
                }
500
                ip++;
501
        }
502
        if (baselen == 0) {
503
                return -EINVAL;
504
        }
505
 
506
        spaces = 8 - baselen;
507
 
508
        if (ext_start) {
509
                extlen = 0;
510
                for (p = ext, ip = ext_start; extlen < 3 && ip < end; ip++) {
511
                        if (utf8 && (*ip & 0x80)) {
512
                                *p++ = '_';
513
                                extlen++;
514
                        } else if (!strchr(skip_chars, *ip)) {
515
                                if (*ip >= 'A' && *ip <= 'Z') {
516
                                        *p = *ip + 32;
517
                                } else {
518
                                        *p = *ip;
519
                                }
520
                                if (strchr(replace_chars, *p)) *p='_';
521
                                extlen++;
522
                                p++;
523
                        }
524
                }
525
        } else {
526
                extlen = 0;
527
        }
528
        ext[extlen] = '\0';
529
        base[baselen] = '\0';
530
 
531
        strcpy(msdos_name, base);
532
        msdos_name[baselen] = '.';
533
        strcpy(&msdos_name[baselen+1], ext);
534
 
535
        totlen = baselen + extlen + (extlen > 0);
536
        res = 0;
537
        if (MSDOS_SB(dir->i_sb)->options.numtail == 0) {
538
                res = vfat_find(dir, msdos_name, totlen, 0, 0, 0, &sinfo);
539
        }
540
        i = 0;
541
        while (res > -1) {
542
                /* Create the next shortname to try */
543
                i++;
544
                if (i == 10000000) return -EEXIST;
545
                sprintf(buf, "%d", i);
546
                sz = strlen(buf);
547
                if (sz + 1 > spaces) {
548
                        baselen = baselen - (sz + 1 - spaces);
549
                        spaces = sz + 1;
550
                }
551
 
552
                strncpy(msdos_name, base, baselen);
553
                msdos_name[baselen] = '~';
554
                strcpy(&msdos_name[baselen+1], buf);
555
                msdos_name[baselen+sz+1] = '.';
556
                strcpy(&msdos_name[baselen+sz+2], ext);
557
 
558
                totlen = baselen + sz + 1 + extlen + (extlen > 0);
559
                res = vfat_find(dir, msdos_name, totlen, 0, 0, 0, &sinfo);
560
        }
561
        res = vfat_format_name(msdos_name, totlen, name_res, 1, utf8);
562
        return res;
563
}
564
 
565
static loff_t vfat_find_free_slots(struct inode *dir,int slots)
566
{
567
        struct super_block *sb = dir->i_sb;
568
        loff_t offset, curr;
569
        struct msdos_dir_entry *de;
570
        struct buffer_head *bh;
571
        struct inode *inode;
572
        int ino;
573
        int row;
574
        int done;
575
        int res;
576
        int added;
577
 
578
        PRINTK(("vfat_find_free_slots: find %d free slots\n", slots));
579
        offset = curr = 0;
580
        bh = NULL;
581
        row = 0;
582
        ino = fat_get_entry(dir,&curr,&bh,&de);
583
 
584
        for (added = 0; added < 2; added++) {
585
                while (ino > -1) {
586
                        done = IS_FREE(de->name);
587
                        if (done) {
588
                                inode = iget(sb,ino);
589
                                if (inode) {
590
                                        /* Directory slots of busy deleted files aren't available yet. */
591
                                        done = !MSDOS_I(inode)->i_busy;
592
                                        /* PRINTK(("inode %d still busy\n", ino)); */
593
                                }
594
                                iput(inode);
595
                        }
596
                        if (done) {
597
                                row++;
598
                                if (row == slots) {
599
                                        fat_brelse(sb, bh);
600
                                        /* printk("----- Free offset at %d\n", offset); */
601
                                        return offset;
602
                                }
603
                        } else {
604
                                row = 0;
605
                                offset = curr;
606
                        }
607
                        ino = fat_get_entry(dir,&curr,&bh,&de);
608
                }
609
 
610
                if ((dir->i_ino == MSDOS_ROOT_INO) &&
611
                    (MSDOS_SB(sb)->fat_bits != 32))
612
                        return -ENOSPC;
613
                if ((res = fat_add_cluster(dir)) < 0) return res;
614
                ino = fat_get_entry(dir,&curr,&bh,&de);
615
        }
616
        return -ENOSPC;
617
}
618
 
619
/* Translate a string, including coded sequences into Unicode */
620
static int
621
xlate_to_uni(const char *name, int len, char *outname, int *outlen,
622
             int escape, int utf8, struct nls_table *nls)
623
{
624
        int i;
625
        const unsigned char *ip;
626
        char *op;
627
        int fill;
628
        unsigned char c1, c2, c3;
629
 
630
        if (utf8) {
631
                *outlen = utf8_mbstowcs((__u16 *) outname, name, PAGE_SIZE);
632
                if (name[len-1] == '.')
633
                        *outlen-=2;
634
                op = &outname[*outlen * sizeof(__u16)];
635
        } else {
636
                if (name[len-1] == '.')
637
                        len--;
638
                op = outname;
639
                if (nls) {
640
                        /* XXX: i is incorrectly computed. */
641
                        for (i = 0, ip = name, op = outname, *outlen = 0;
642
                             i < len && *outlen <= 260; i++, *outlen += 1)
643
                        {
644
                                if (escape && (*ip == ':')) {
645
                                        if (i > len - 4) return -EINVAL;
646
                                        c1 = fat_esc2uni[ip[1]];
647
                                        c2 = fat_esc2uni[ip[2]];
648
                                        c3 = fat_esc2uni[ip[3]];
649
                                        if (c1 == 255 || c2 == 255 || c3 == 255)
650
                                                return -EINVAL;
651
                                        *op++ = (c1 << 4) + (c2 >> 2);
652
                                        *op++ = ((c2 & 0x3) << 6) + c3;
653
                                        ip += 4;
654
                                } else {
655
                                        *op++ = nls->charset2uni[*ip].uni1;
656
                                        *op++ = nls->charset2uni[*ip].uni2;
657
                                        ip++;
658
                                }
659
                        }
660
                } else {
661
                        for (i = 0, ip = name, op = outname, *outlen = 0;
662
                             i < len && *outlen <= 260; i++, *outlen += 1)
663
                        {
664
                                *op++ = *ip++;
665
                                *op++ = 0;
666
                        }
667
                }
668
        }
669
        if (*outlen > 260)
670
                return -ENAMETOOLONG;
671
 
672
        if (*outlen % 13) {
673
                *op++ = 0;
674
                *op++ = 0;
675
                *outlen += 1;
676
                if (*outlen % 13) {
677
                        fill = 13 - (*outlen % 13);
678
                        for (i = 0; i < fill; i++) {
679
                                *op++ = 0xff;
680
                                *op++ = 0xff;
681
                        }
682
                        *outlen += fill;
683
                }
684
        }
685
 
686
        return 0;
687
}
688
 
689
static int
690
vfat_fill_long_slots(struct msdos_dir_slot *ds, const char *name, int len,
691
                     char *msdos_name, int *slots,
692
                     int uni_xlate, int utf8, struct nls_table *nls)
693
{
694
        struct msdos_dir_slot *ps;
695
        struct msdos_dir_entry *de;
696
        int res;
697
        int slot;
698
        unsigned char cksum;
699
        char *uniname;
700
        const char *ip;
701
        unsigned long page;
702
        int unilen;
703
        int i;
704
        loff_t offset;
705
 
706
        if (name[len-1] == '.') len--;
707
        if(!(page = __get_free_page(GFP_KERNEL)))
708
                return -ENOMEM;
709
        uniname = (char *) page;
710
        res = xlate_to_uni(name, len, uniname, &unilen, uni_xlate, utf8, nls);
711
        if (res < 0) {
712
                free_page(page);
713
                return res;
714
        }
715
 
716
        *slots = unilen / 13;
717
        for (cksum = i = 0; i < 11; i++) {
718
                cksum = (((cksum&1)<<7)|((cksum&0xfe)>>1)) + msdos_name[i];
719
        }
720
        PRINTK(("vfat_fill_long_slots 3: slots=%d\n",*slots));
721
 
722
        for (ps = ds, slot = *slots; slot > 0; slot--, ps++) {
723
                int end, j;
724
 
725
                PRINTK(("vfat_fill_long_slots 4\n"));
726
                ps->id = slot;
727
                ps->attr = ATTR_EXT;
728
                ps->reserved = 0;
729
                ps->alias_checksum = cksum;
730
                ps->start = 0;
731
                PRINTK(("vfat_fill_long_slots 5: uniname=%s\n",uniname));
732
                offset = (slot - 1) * 26;
733
                ip = &uniname[offset];
734
                j = offset;
735
                end = 0;
736
                for (i = 0; i < 10; i += 2) {
737
                        ps->name0_4[i] = *ip++;
738
                        ps->name0_4[i+1] = *ip++;
739
                }
740
                PRINTK(("vfat_fill_long_slots 6\n"));
741
                for (i = 0; i < 12; i += 2) {
742
                        ps->name5_10[i] = *ip++;
743
                        ps->name5_10[i+1] = *ip++;
744
                }
745
                PRINTK(("vfat_fill_long_slots 7\n"));
746
                for (i = 0; i < 4; i += 2) {
747
                        ps->name11_12[i] = *ip++;
748
                        ps->name11_12[i+1] = *ip++;
749
                }
750
        }
751
        PRINTK(("vfat_fill_long_slots 8\n"));
752
        ds[0].id |= 0x40;
753
 
754
        de = (struct msdos_dir_entry *) ps;
755
        PRINTK(("vfat_fill_long_slots 9\n"));
756
        strncpy(de->name, msdos_name, MSDOS_NAME);
757
 
758
        free_page(page);
759
        return 0;
760
}
761
 
762
static int vfat_build_slots(struct inode *dir,const char *name,int len,
763
     struct msdos_dir_slot *ds, int *slots, int *is_long)
764
{
765
        struct msdos_dir_entry *de;
766
        char msdos_name[MSDOS_NAME];
767
        int res, xlate, utf8;
768
        struct nls_table *nls;
769
 
770
        PRINTK(("Entering vfat_build_slots: name=%s, len=%d\n", name, len));
771
        de = (struct msdos_dir_entry *) ds;
772
        xlate = MSDOS_SB(dir->i_sb)->options.unicode_xlate;
773
        utf8 = MSDOS_SB(dir->i_sb)->options.utf8;
774
        nls = MSDOS_SB(dir->i_sb)->nls_io;
775
 
776
        *slots = 1;
777
        *is_long = 0;
778
        if (len == 1 && name[0] == '.') {
779
                strncpy(de->name, MSDOS_DOT, MSDOS_NAME);
780
        } else if (len == 2 && name[0] == '.' && name[1] == '.') {
781
                strncpy(de->name, MSDOS_DOT, MSDOS_NAME);
782
        } else {
783
                PRINTK(("vfat_build_slots 4\n"));
784
                res = vfat_valid_shortname(name, len, 1, utf8);
785
                if (res > -1) {
786
                        PRINTK(("vfat_build_slots 5a\n"));
787
                        res = vfat_format_name(name, len, de->name, 1, utf8);
788
                        PRINTK(("vfat_build_slots 5b\n"));
789
                } else {
790
                        res = vfat_create_shortname(dir, name, len, msdos_name, utf8);
791
                        if (res < 0) {
792
                                return res;
793
                        }
794
 
795
                        res = vfat_valid_longname(name, len, 1, xlate);
796
                        if (res < 0) {
797
                                return res;
798
                        }
799
 
800
                        *is_long = 1;
801
 
802
                        return vfat_fill_long_slots(ds, name, len, msdos_name,
803
                                                    slots, xlate, utf8, nls);
804
                }
805
        }
806
        return 0;
807
}
808
 
809
static int vfat_readdir_cb(
810
        filldir_t filldir,
811
        void * buf,
812
        const char * name,
813
        int name_len,
814
        int is_long,
815
        off_t offset,
816
        off_t short_offset,
817
        int long_slots,
818
        ino_t ino)
819
{
820
        struct vfat_find_info *vf = (struct vfat_find_info *) buf;
821
        const char *s1, *s2;
822
        int i;
823
 
824
#ifdef DEBUG
825
        if (debug) printk("cb: vf.name=%s, len=%d, name=%s, name_len=%d\n",
826
                          vf->name, vf->len, name, name_len);
827
#endif
828
 
829
        /* Filenames cannot end in '.' or we treat like it has none */
830
        if (vf->len != name_len) {
831
                if ((vf->len != name_len + 1) || (vf->name[name_len] != '.')) {
832
                        return 0;
833
                }
834
                if (name_len == 2 && name[0] == '.' && name[1] == '.') {
835
                        return 0;
836
                }
837
        }
838
 
839
        s1 = name; s2 = vf->name;
840
        for (i = 0; i < name_len; i++) {
841
                if (vf->new_filename && !vf->posix) {
842
                        if (tolower(*s1) != tolower(*s2))
843
                                return 0;
844
                } else {
845
                        if (*s1 != *s2)
846
                                return 0;
847
                }
848
                s1++; s2++;
849
        }
850
        vf->found = 1;
851
        vf->is_long = is_long;
852
        vf->offset = (offset == 2) ? 0 : offset;
853
        vf->short_offset = (short_offset == 2) ? 0 : short_offset;
854
        vf->long_slots = long_slots;
855
        vf->ino = ino;
856
        return -1;
857
}
858
 
859
static int vfat_find(struct inode *dir,const char *name,int len,
860
    int find_long, int new_filename,int is_dir,struct slot_info *sinfo_out)
861
{
862
        struct super_block *sb = dir->i_sb;
863
        struct vfat_find_info vf;
864
        struct file fil;
865
        struct buffer_head *bh;
866
        struct msdos_dir_entry *de;
867
        struct msdos_dir_slot *ps;
868
        loff_t offset;
869
        struct msdos_dir_slot *ds;
870
        int is_long;
871
        int slots, slot;
872
        int res;
873
 
874
        PRINTK(("Entering vfat_find\n"));
875
 
876
        ds = (struct msdos_dir_slot *)
877
            kmalloc(sizeof(struct msdos_dir_slot)*MSDOS_SLOTS, GFP_KERNEL);
878
        if (ds == NULL) return -ENOMEM;
879
 
880
        fil.f_pos = 0;
881
        vf.name = name;
882
        vf.len = len;
883
        vf.new_filename = new_filename;
884
        vf.found = 0;
885
        vf.posix = MSDOS_SB(sb)->options.posixfs;
886
        res = fat_readdirx(dir,&fil,(void *)&vf,vfat_readdir_cb,NULL,1,find_long,0);
887
        PRINTK(("vfat_find: Debug 1\n"));
888
        if (res < 0) goto cleanup;
889
        if (vf.found) {
890
                if (new_filename) {
891
                        res = -EEXIST;
892
                        goto cleanup;
893
                }
894
                sinfo_out->longname_offset = vf.offset;
895
                sinfo_out->shortname_offset = vf.short_offset;
896
                sinfo_out->is_long = vf.is_long;
897
                sinfo_out->long_slots = vf.long_slots;
898
                sinfo_out->total_slots = vf.long_slots + 1;
899
                sinfo_out->ino = vf.ino;
900
 
901
                PRINTK(("vfat_find: Debug 2\n"));
902
                res = 0;
903
                goto cleanup;
904
        }
905
 
906
        PRINTK(("vfat_find: Debug 3\n"));
907
        if (!vf.found && !new_filename) {
908
                res = -ENOENT;
909
                goto cleanup;
910
        }
911
 
912
        res = vfat_build_slots(dir, name, len, ds, &slots, &is_long);
913
        if (res < 0) goto cleanup;
914
 
915
        de = (struct msdos_dir_entry *) ds;
916
 
917
        bh = NULL;
918
        if (new_filename) {
919
                PRINTK(("vfat_find: create file 1\n"));
920
                if (is_long) slots++;
921
                offset = vfat_find_free_slots(dir, slots);
922
                if (offset < 0) {
923
                        res = offset;
924
                        goto cleanup;
925
                }
926
 
927
                PRINTK(("vfat_find: create file 2\n"));
928
                /* Now create the new entry */
929
                bh = NULL;
930
                for (slot = 0, ps = ds; slot < slots; slot++, ps++) {
931
                        PRINTK(("vfat_find: create file 3, slot=%d\n",slot));
932
                        sinfo_out->ino = fat_get_entry(dir,&offset,&bh,&de);
933
                        if (sinfo_out->ino < 0) {
934
                                PRINTK(("vfat_find: problem\n"));
935
                                res = sinfo_out->ino;
936
                                goto cleanup;
937
                        }
938
                        memcpy(de, ps, sizeof(struct msdos_dir_slot));
939
                        fat_mark_buffer_dirty(sb, bh, 1);
940
                }
941
 
942
                PRINTK(("vfat_find: create file 4\n"));
943
                dir->i_ctime = dir->i_mtime = dir->i_atime = CURRENT_TIME;
944
                dir->i_dirt = 1;
945
 
946
                PRINTK(("vfat_find: create file 5\n"));
947
 
948
                fat_date_unix2dos(dir->i_mtime,&de->time,&de->date);
949
                de->ctime_ms = 0;
950
                de->ctime = de->time;
951
                de->adate = de->cdate = de->date;
952
                de->start = 0;
953
                de->starthi = 0;
954
                de->size = 0;
955
                de->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
956
                de->lcase = CASE_LOWER_BASE | CASE_LOWER_EXT;
957
 
958
 
959
                fat_mark_buffer_dirty(sb, bh, 1);
960
                fat_brelse(sb, bh);
961
 
962
                sinfo_out->is_long = (slots > 1) ? 1 : 0;
963
                if (sinfo_out->is_long) {
964
                        sinfo_out->long_slots = slots - 1;
965
                } else {
966
                        sinfo_out->long_slots = 0;
967
                }
968
                sinfo_out->total_slots = slots;
969
                sinfo_out->shortname_offset = offset - sizeof(struct msdos_dir_slot);
970
                sinfo_out->longname_offset = offset - sizeof(struct msdos_dir_slot) * slots;
971
                res = 0;
972
        } else {
973
                res = -ENOENT;
974
        }
975
 
976
cleanup:
977
        kfree(ds);
978
        return res;
979
}
980
 
981
int vfat_lookup(struct inode *dir,const char *name,int len,
982
    struct inode **result)
983
{
984
        int res, ino;
985
        struct inode *next;
986
        struct slot_info sinfo;
987
 
988
        PRINTK (("vfat_lookup: name=%s, len=%d\n", name, len));
989
 
990
        *result = NULL;
991
        if (!dir) return -ENOENT;
992
        if (!S_ISDIR(dir->i_mode)) {
993
                iput(dir);
994
                return -ENOENT;
995
        }
996
        PRINTK (("vfat_lookup 2\n"));
997
        if (len == 1 && name[0] == '.') {
998
                *result = dir;
999
                return 0;
1000
        }
1001
        if (len == 2 && name[0] == '.' && name[1] == '.') {
1002
                ino = fat_parent_ino(dir,0);
1003
                iput(dir);
1004
                if (ino < 0) return ino;
1005
                if (!(*result = iget(dir->i_sb,ino))) return -EACCES;
1006
                return 0;
1007
        }
1008
        if (dcache_lookup(dir, name, len, (unsigned long *) &ino) && ino) {
1009
                iput(dir);
1010
                if (!(*result = iget(dir->i_sb, ino)))
1011
                        return -EACCES;
1012
                return 0;
1013
        }
1014
        PRINTK (("vfat_lookup 3\n"));
1015
        if ((res = vfat_find(dir,name,len,1,0,0,&sinfo)) < 0) {
1016
                iput(dir);
1017
                return res;
1018
        }
1019
        PRINTK (("vfat_lookup 4.5\n"));
1020
        if (!(*result = iget(dir->i_sb,sinfo.ino))) {
1021
                iput(dir);
1022
                return -EACCES;
1023
        }
1024
        PRINTK (("vfat_lookup 5\n"));
1025
        if (!(*result)->i_sb ||
1026
            ((*result)->i_sb->s_magic != MSDOS_SUPER_MAGIC)) {
1027
                /* crossed a mount point into a non-msdos fs */
1028
                iput(dir);
1029
                return 0;
1030
        }
1031
        if (MSDOS_I(*result)->i_busy) { /* mkdir in progress */
1032
                iput(*result);
1033
                iput(dir);
1034
                return -ENOENT;
1035
        }
1036
        PRINTK (("vfat_lookup 6\n"));
1037
        while (MSDOS_I(*result)->i_old) {
1038
                next = MSDOS_I(*result)->i_old;
1039
                iput(*result);
1040
                if (!(*result = iget(next->i_sb,next->i_ino))) {
1041
                        fat_fs_panic(dir->i_sb,"vfat_lookup: Can't happen");
1042
                        iput(dir);
1043
                        return -ENOENT;
1044
                }
1045
        }
1046
        iput(dir);
1047
        return 0;
1048
}
1049
 
1050
 
1051
static int vfat_create_entry(struct inode *dir,const char *name,int len,
1052
    int is_dir, struct inode **result)
1053
{
1054
        struct super_block *sb = dir->i_sb;
1055
        int res,ino;
1056
        loff_t offset;
1057
        struct buffer_head *bh;
1058
        struct msdos_dir_entry *de;
1059
        struct slot_info sinfo;
1060
 
1061
        PRINTK(("vfat_create_entry 1\n"));
1062
        res = vfat_find(dir, name, len, 1, 1, is_dir, &sinfo);
1063
        if (res < 0) {
1064
                return res;
1065
        }
1066
 
1067
        offset = sinfo.shortname_offset;
1068
 
1069
        PRINTK(("vfat_create_entry 2\n"));
1070
        bh = NULL;
1071
        ino = fat_get_entry(dir, &offset, &bh, &de);
1072
        if (ino < 0) {
1073
                PRINTK(("vfat_mkdir problem\n"));
1074
                if (bh)
1075
                        fat_brelse(sb, bh);
1076
                return ino;
1077
        }
1078
        PRINTK(("vfat_create_entry 3\n"));
1079
 
1080
        if ((*result = iget(dir->i_sb,ino)) != NULL)
1081
                vfat_read_inode(*result);
1082
        fat_brelse(sb, bh);
1083
        if (!*result)
1084
                return -EIO;
1085
        (*result)->i_mtime = (*result)->i_atime = (*result)->i_ctime =
1086
            CURRENT_TIME;
1087
        (*result)->i_dirt = 1;
1088
        (*result)->i_version = ++event;
1089
        dir->i_version = event;
1090
        dcache_add(dir, name, len, ino);
1091
 
1092
        return 0;
1093
}
1094
 
1095
int vfat_create(struct inode *dir,const char *name,int len,int mode,
1096
        struct inode **result)
1097
{
1098
        int res;
1099
 
1100
        if (!dir) return -ENOENT;
1101
 
1102
        fat_lock_creation();
1103
        res = vfat_create_entry(dir,name,len,0,result);
1104
        if (res < 0) PRINTK(("vfat_create: unable to get new entry\n"));
1105
 
1106
        fat_unlock_creation();
1107
        iput(dir);
1108
        return res;
1109
}
1110
 
1111
static int vfat_create_a_dotdir(struct inode *dir,struct inode *parent,
1112
     struct buffer_head *bh,
1113
     struct msdos_dir_entry *de,int ino,const char *name, int isdot)
1114
{
1115
        struct super_block *sb = dir->i_sb;
1116
        struct inode *dot;
1117
 
1118
        PRINTK(("vfat_create_a_dotdir 1\n"));
1119
 
1120
        /*
1121
         * XXX all times should be set by caller upon successful completion.
1122
         */
1123
        dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
1124
        dir->i_dirt = 1;
1125
        memcpy(de->name,name,MSDOS_NAME);
1126
        de->lcase = 0;
1127
        de->attr = ATTR_DIR;
1128
        de->start = 0;
1129
        de->starthi = 0;
1130
        fat_date_unix2dos(dir->i_mtime,&de->time,&de->date);
1131
        de->ctime_ms = 0;
1132
        de->ctime = de->time;
1133
        de->adate = de->cdate = de->date;
1134
        de->size = 0;
1135
        fat_mark_buffer_dirty(sb, bh, 1);
1136
        if ((dot = iget(dir->i_sb,ino)) != NULL)
1137
                vfat_read_inode(dot);
1138
        if (!dot) return -EIO;
1139
        dot->i_mtime = dot->i_atime = CURRENT_TIME;
1140
        dot->i_dirt = 1;
1141
        if (isdot) {
1142
                dot->i_size = dir->i_size;
1143
                MSDOS_I(dot)->i_start = MSDOS_I(dir)->i_start;
1144
                MSDOS_I(dot)->i_logstart = MSDOS_I(dir)->i_logstart;
1145
                dot->i_nlink = dir->i_nlink;
1146
        } else {
1147
                dot->i_size = parent->i_size;
1148
                MSDOS_I(dot)->i_start = MSDOS_I(parent)->i_start;
1149
                MSDOS_I(dot)->i_logstart = MSDOS_I(parent)->i_logstart;
1150
                dot->i_nlink = parent->i_nlink;
1151
        }
1152
 
1153
        iput(dot);
1154
 
1155
        PRINTK(("vfat_create_a_dotdir 2\n"));
1156
        return 0;
1157
}
1158
 
1159
static int vfat_create_dotdirs(struct inode *dir, struct inode *parent)
1160
{
1161
        struct super_block *sb = dir->i_sb;
1162
        int res;
1163
        struct buffer_head *bh;
1164
        struct msdos_dir_entry *de;
1165
        loff_t offset;
1166
 
1167
        PRINTK(("vfat_create_dotdirs 1\n"));
1168
        if ((res = fat_add_cluster(dir)) < 0) return res;
1169
 
1170
        PRINTK(("vfat_create_dotdirs 2\n"));
1171
        offset = 0;
1172
        bh = NULL;
1173
        if ((res = fat_get_entry(dir,&offset,&bh,&de)) < 0) return res;
1174
 
1175
        PRINTK(("vfat_create_dotdirs 3\n"));
1176
        res = vfat_create_a_dotdir(dir, parent, bh, de, res, MSDOS_DOT, 1);
1177
        PRINTK(("vfat_create_dotdirs 4\n"));
1178
        if (res < 0) {
1179
                fat_brelse(sb, bh);
1180
                return res;
1181
        }
1182
        PRINTK(("vfat_create_dotdirs 5\n"));
1183
 
1184
        if ((res = fat_get_entry(dir,&offset,&bh,&de)) < 0) {
1185
                fat_brelse(sb, bh);
1186
                return res;
1187
        }
1188
        PRINTK(("vfat_create_dotdirs 6\n"));
1189
 
1190
        res = vfat_create_a_dotdir(dir, parent, bh, de, res, MSDOS_DOTDOT, 0);
1191
        PRINTK(("vfat_create_dotdirs 7\n"));
1192
        fat_brelse(sb, bh);
1193
 
1194
        return res;
1195
}
1196
 
1197
/***** See if directory is empty */
1198
static int vfat_empty(struct inode *dir)
1199
{
1200
        struct super_block *sb = dir->i_sb;
1201
        loff_t pos;
1202
        struct buffer_head *bh;
1203
        struct msdos_dir_entry *de;
1204
 
1205
        if (dir->i_count > 1)
1206
                return -EBUSY;
1207
        if (MSDOS_I(dir)->i_start) { /* may be zero in mkdir */
1208
                pos = 0;
1209
                bh = NULL;
1210
                while (fat_get_entry(dir,&pos,&bh,&de) > -1) {
1211
                        /* Skip extended filename entries */
1212
                        if (de->attr == ATTR_EXT) continue;
1213
 
1214
                        if (!IS_FREE(de->name) && strncmp(de->name,MSDOS_DOT,
1215
                            MSDOS_NAME) && strncmp(de->name,MSDOS_DOTDOT,
1216
                            MSDOS_NAME)) {
1217
                                fat_brelse(sb, bh);
1218
                                return -ENOTEMPTY;
1219
                        }
1220
                }
1221
                if (bh)
1222
                        fat_brelse(sb, bh);
1223
        }
1224
        return 0;
1225
}
1226
 
1227
static int vfat_rmdir_free_ino(struct inode *dir,struct buffer_head *bh,
1228
     struct msdos_dir_entry *de,int ino)
1229
{
1230
        struct super_block *sb = dir->i_sb;
1231
        struct inode *inode;
1232
        int res;
1233
 
1234
        if (ino < 0) return -EINVAL;
1235
        if (!(inode = iget(dir->i_sb,ino))) return -ENOENT;
1236
        if (!S_ISDIR(inode->i_mode)) {
1237
                iput(inode);
1238
                return -ENOTDIR;
1239
        }
1240
        if (dir->i_dev != inode->i_dev || dir == inode) {
1241
                iput(inode);
1242
                return -EBUSY;
1243
        }
1244
        res = vfat_empty(inode);
1245
        if (res) {
1246
                iput(inode);
1247
                return res;
1248
        }
1249
        inode->i_nlink = 0;
1250
        inode->i_mtime = dir->i_mtime = CURRENT_TIME;
1251
        inode->i_atime = dir->i_atime = CURRENT_TIME;
1252
        dir->i_nlink--;
1253
        inode->i_dirt = dir->i_dirt = 1;
1254
        de->name[0] = DELETED_FLAG;
1255
        fat_mark_buffer_dirty(sb, bh, 1);
1256
        iput(inode);
1257
 
1258
        return 0;
1259
}
1260
 
1261
static int vfat_unlink_free_ino(struct inode *dir,struct buffer_head *bh,
1262
     struct msdos_dir_entry *de,int ino,int nospc)
1263
{
1264
        struct super_block *sb = dir->i_sb;
1265
        struct inode *inode;
1266
        if (!(inode = iget(dir->i_sb,ino))) return -ENOENT;
1267
        if ((!S_ISREG(inode->i_mode) && nospc) || IS_IMMUTABLE(inode)) {
1268
                iput(inode);
1269
                return -EPERM;
1270
        }
1271
        inode->i_nlink = 0;
1272
        inode->i_mtime = dir->i_mtime = CURRENT_TIME;
1273
        inode->i_atime = dir->i_atime = CURRENT_TIME;
1274
        dir->i_version = ++event;
1275
        MSDOS_I(inode)->i_busy = 1;
1276
        inode->i_dirt = dir->i_dirt = 1;
1277
        de->name[0] = DELETED_FLAG;
1278
        fat_mark_buffer_dirty(sb, bh, 1);
1279
 
1280
        iput(inode);
1281
        return 0;
1282
}
1283
 
1284
static int vfat_remove_entry(struct inode *dir,struct slot_info *sinfo,
1285
     struct buffer_head **bh,struct msdos_dir_entry **de,
1286
     int is_dir,int nospc)
1287
{
1288
        struct super_block *sb = dir->i_sb;
1289
        loff_t offset;
1290
        int res, i;
1291
 
1292
        /* remove the shortname */
1293
        offset = sinfo->shortname_offset;
1294
        res = fat_get_entry(dir, &offset, bh, de);
1295
        if (res < 0) return res;
1296
        if (is_dir) {
1297
                res = vfat_rmdir_free_ino(dir,*bh,*de,res);
1298
        } else {
1299
                res = vfat_unlink_free_ino(dir,*bh,*de,res,nospc);
1300
        }
1301
        if (res < 0) return res;
1302
 
1303
        /* remove the longname */
1304
        offset = sinfo->longname_offset;
1305
        for (i = sinfo->long_slots; i > 0; --i) {
1306
                res = fat_get_entry(dir, &offset, bh, de);
1307
                if (res < 0) {
1308
                        printk("vfat_remove_entry: problem 1\n");
1309
                        continue;
1310
                }
1311
                (*de)->name[0] = DELETED_FLAG;
1312
                (*de)->attr = 0;
1313
                fat_mark_buffer_dirty(sb, *bh, 1);
1314
        }
1315
        return 0;
1316
}
1317
 
1318
 
1319
static int vfat_rmdirx(struct inode *dir,const char *name,int len)
1320
{
1321
        struct super_block *sb = dir->i_sb;
1322
        int res;
1323
        struct buffer_head *bh;
1324
        struct msdos_dir_entry *de;
1325
        struct slot_info sinfo;
1326
 
1327
        bh = NULL;
1328
        res = -EPERM;
1329
        if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.')))
1330
                goto rmdir_done;
1331
        res = vfat_find(dir,name,len,1,0,0,&sinfo);
1332
 
1333
        if (res >= 0 && sinfo.total_slots > 0) {
1334
                res = vfat_remove_entry(dir,&sinfo,&bh,&de,1,0);
1335
                if (res > 0) {
1336
                        res = 0;
1337
                }
1338
                dir->i_version = ++event;
1339
        }
1340
 
1341
rmdir_done:
1342
        fat_brelse(sb, bh);
1343
        return res;
1344
}
1345
 
1346
/***** Remove a directory */
1347
int vfat_rmdir(struct inode *dir,const char *name,int len)
1348
{
1349
        int res;
1350
 
1351
        res = vfat_rmdirx(dir, name, len);
1352
        iput(dir);
1353
        return res;
1354
}
1355
 
1356
static int vfat_unlinkx(
1357
        struct inode *dir,
1358
        const char *name,
1359
        int len,
1360
        int nospc)      /* Flag special file ? */
1361
{
1362
        struct super_block *sb = dir->i_sb;
1363
        int res;
1364
        struct buffer_head *bh;
1365
        struct msdos_dir_entry *de;
1366
        struct slot_info sinfo;
1367
 
1368
        bh = NULL;
1369
        res = vfat_find(dir,name,len,1,0,0,&sinfo);
1370
 
1371
        if (res >= 0 && sinfo.total_slots > 0) {
1372
                res = vfat_remove_entry(dir,&sinfo,&bh,&de,0,nospc);
1373
                if (res > 0) {
1374
                        res = 0;
1375
                }
1376
        }
1377
 
1378
        fat_brelse(sb, bh);
1379
        return res;
1380
}
1381
 
1382
 
1383
int vfat_mkdir(struct inode *dir,const char *name,int len,int mode)
1384
{
1385
        struct inode *inode;
1386
        int res;
1387
 
1388
        fat_lock_creation();
1389
        if ((res = vfat_create_entry(dir,name,len,1,&inode)) < 0) {
1390
                fat_unlock_creation();
1391
                iput(dir);
1392
                return res;
1393
        }
1394
 
1395
        dir->i_nlink++;
1396
        inode->i_nlink = 2; /* no need to mark them dirty */
1397
        MSDOS_I(inode)->i_busy = 1; /* prevent lookups */
1398
 
1399
        res = vfat_create_dotdirs(inode, dir);
1400
        fat_unlock_creation();
1401
        MSDOS_I(inode)->i_busy = 0;
1402
        iput(inode);
1403
        iput(dir);
1404
        if (res < 0) {
1405
                if (vfat_rmdir(dir,name,len) < 0)
1406
                        fat_fs_panic(dir->i_sb,"rmdir in mkdir failed");
1407
        }
1408
        return res;
1409
}
1410
 
1411
/***** Unlink, as called for msdosfs */
1412
int vfat_unlink(struct inode *dir,const char *name,int len)
1413
{
1414
        int res;
1415
 
1416
        res = vfat_unlinkx (dir,name,len,1);
1417
        iput(dir);
1418
        return res;
1419
}
1420
 
1421
/***** Unlink, as called for uvfatfs */
1422
int vfat_unlink_uvfat(struct inode *dir,const char *name,int len)
1423
{
1424
        int res;
1425
 
1426
        res = vfat_unlinkx (dir,name,len,0);
1427
        iput(dir);
1428
        return res;
1429
}
1430
 
1431
int vfat_rename(struct inode *old_dir,const char *old_name,int old_len,
1432
        struct inode *new_dir,const char *new_name,int new_len,int must_be_dir)
1433
{
1434
        struct super_block *sb = old_dir->i_sb;
1435
        struct buffer_head *old_bh,*new_bh,*dotdot_bh;
1436
        struct msdos_dir_entry *old_de,*new_de,*dotdot_de;
1437
        loff_t old_offset,new_offset,old_longname_offset;
1438
        int old_slots,old_ino,new_ino,dotdot_ino,ino;
1439
        struct inode *old_inode, *new_inode, *dotdot_inode, *walk;
1440
        int res, is_dir, i;
1441
        int locked = 0;
1442
        struct slot_info sinfo;
1443
 
1444
        PRINTK(("vfat_rename 1\n"));
1445
        if (old_dir == new_dir && old_len == new_len &&
1446
            strncmp(old_name, new_name, old_len) == 0)
1447
                return 0;
1448
 
1449
        old_bh = new_bh = NULL;
1450
        old_inode = new_inode = NULL;
1451
        res = vfat_find(old_dir,old_name,old_len,1,0,0,&sinfo);
1452
        PRINTK(("vfat_rename 2\n"));
1453
        if (res < 0) goto rename_done;
1454
 
1455
        old_slots = sinfo.total_slots;
1456
        old_longname_offset = sinfo.longname_offset;
1457
        old_offset = sinfo.shortname_offset;
1458
        old_ino = sinfo.ino;
1459
        res = fat_get_entry(old_dir, &old_offset, &old_bh, &old_de);
1460
        PRINTK(("vfat_rename 3\n"));
1461
        if (res < 0) goto rename_done;
1462
 
1463
        res = -ENOENT;
1464
        if (!(old_inode = iget(old_dir->i_sb,old_ino)))
1465
                goto rename_done;
1466
        is_dir = S_ISDIR(old_inode->i_mode);
1467
        if (must_be_dir && !is_dir)
1468
                goto rename_done;
1469
        if (is_dir) {
1470
                if ((old_dir->i_dev != new_dir->i_dev) ||
1471
                    (old_ino == new_dir->i_ino)) {
1472
                        res = -EINVAL;
1473
                        goto rename_done;
1474
                }
1475
                if (!(walk = iget(new_dir->i_sb,new_dir->i_ino))) return -EIO;
1476
                /* prevent moving directory below itself */
1477
                while (walk->i_ino != MSDOS_ROOT_INO) {
1478
                        ino = fat_parent_ino(walk,1);
1479
                        iput(walk);
1480
                        if (ino < 0) {
1481
                                res = ino;
1482
                                goto rename_done;
1483
                        }
1484
                        if (ino == old_ino) {
1485
                                res = -EINVAL;
1486
                                goto rename_done;
1487
                        }
1488
                        if (!(walk = iget(new_dir->i_sb,ino))) {
1489
                                res = -EIO;
1490
                                goto rename_done;
1491
                        }
1492
                }
1493
                iput(walk);
1494
        }
1495
 
1496
        res = vfat_find(new_dir,new_name,new_len,1,0,is_dir,&sinfo);
1497
 
1498
        PRINTK(("vfat_rename 4\n"));
1499
        if (res > -1) {
1500
                int new_is_dir;
1501
 
1502
                PRINTK(("vfat_rename 5\n"));
1503
                /* Filename currently exists.  Need to delete it */
1504
                new_offset = sinfo.shortname_offset;
1505
                res = fat_get_entry(new_dir, &new_offset, &new_bh, &new_de);
1506
                PRINTK(("vfat_rename 6\n"));
1507
                if (res < 0) goto rename_done;
1508
 
1509
                if (!(new_inode = iget(new_dir->i_sb,res)))
1510
                        goto rename_done;
1511
                new_is_dir = S_ISDIR(new_inode->i_mode);
1512
                iput(new_inode);
1513
                if (new_is_dir) {
1514
                        PRINTK(("vfat_rename 7\n"));
1515
                        res = vfat_rmdirx(new_dir,new_name,new_len);
1516
                        PRINTK(("vfat_rename 8\n"));
1517
                        if (res < 0) goto rename_done;
1518
                } else {
1519
                        /* Is this the same file, different case? */
1520
                        if (new_inode != old_inode) {
1521
                                PRINTK(("vfat_rename 9\n"));
1522
                                res = vfat_unlinkx(new_dir,new_name,new_len,1);
1523
                                PRINTK(("vfat_rename 10\n"));
1524
                                if (res < 0) goto rename_done;
1525
                        }
1526
                }
1527
        }
1528
 
1529
        PRINTK(("vfat_rename 11\n"));
1530
        fat_lock_creation(); locked = 1;
1531
        res = vfat_find(new_dir,new_name,new_len,1,1,is_dir,&sinfo);
1532
 
1533
        PRINTK(("vfat_rename 12\n"));
1534
        if (res < 0) goto rename_done;
1535
 
1536
        new_offset = sinfo.shortname_offset;
1537
        new_ino = sinfo.ino;
1538
        res = fat_get_entry(new_dir, &new_offset, &new_bh, &new_de);
1539
        PRINTK(("vfat_rename 13\n"));
1540
        if (res < 0) goto rename_done;
1541
 
1542
        new_de->attr = old_de->attr;
1543
        new_de->time = old_de->time;
1544
        new_de->date = old_de->date;
1545
        new_de->ctime_ms = old_de->ctime_ms;
1546
        new_de->cdate = old_de->cdate;
1547
        new_de->adate = old_de->adate;
1548
        new_de->start = old_de->start;
1549
        new_de->starthi = old_de->starthi;
1550
        new_de->size = old_de->size;
1551
 
1552
        if (!(new_inode = iget(new_dir->i_sb,new_ino))) goto rename_done;
1553
        PRINTK(("vfat_rename 14\n"));
1554
 
1555
        /* At this point, we have the inodes of the old file and the
1556
         * new file.  We need to transfer all information from the old
1557
         * inode to the new inode and then delete the slots of the old
1558
         * entry
1559
         */
1560
 
1561
        vfat_read_inode(new_inode);
1562
        MSDOS_I(old_inode)->i_busy = 1;
1563
        MSDOS_I(old_inode)->i_linked = new_inode;
1564
        MSDOS_I(new_inode)->i_oldlink = old_inode;
1565
        fat_cache_inval_inode(old_inode);
1566
        PRINTK(("vfat_rename 15: old_slots=%d\n",old_slots));
1567
        old_inode->i_dirt = 1;
1568
        old_dir->i_version = ++event;
1569
 
1570
        /* remove the old entry */
1571
        for (i = old_slots; i > 0; --i) {
1572
                res = fat_get_entry(old_dir, &old_longname_offset, &old_bh, &old_de);
1573
                if (res < 0) {
1574
                        printk("vfat_unlinkx: problem 1\n");
1575
                        continue;
1576
                }
1577
                old_de->name[0] = DELETED_FLAG;
1578
                old_de->attr = 0;
1579
                fat_mark_buffer_dirty(sb, old_bh, 1);
1580
        }
1581
        PRINTK(("vfat_rename 15b\n"));
1582
 
1583
        fat_mark_buffer_dirty(sb, new_bh, 1);
1584
        dcache_add(new_dir, new_name, new_len, new_ino);
1585
 
1586
        /* XXX: There is some code in the original MSDOS rename that
1587
         * is not duplicated here and it might cause a problem in
1588
         * certain circumstances.
1589
         */
1590
 
1591
        if (S_ISDIR(old_inode->i_mode)) {
1592
                if ((res = fat_scan(old_inode,MSDOS_DOTDOT,&dotdot_bh,
1593
                    &dotdot_de,&dotdot_ino,SCAN_ANY)) < 0) goto rename_done;
1594
                if (!(dotdot_inode = iget(old_inode->i_sb,dotdot_ino))) {
1595
                        fat_brelse(sb, dotdot_bh);
1596
                        res = -EIO;
1597
                        goto rename_done;
1598
                }
1599
                MSDOS_I(dotdot_inode)->i_start = MSDOS_I(new_dir)->i_start;
1600
                MSDOS_I(dotdot_inode)->i_logstart = MSDOS_I(new_dir)->i_logstart;
1601
                dotdot_de->start = CT_LE_W(MSDOS_I(new_dir)->i_logstart);
1602
                dotdot_de->starthi = CT_LE_W((MSDOS_I(new_dir)->i_logstart) >> 16);
1603
                dotdot_inode->i_dirt = 1;
1604
                fat_mark_buffer_dirty(sb, dotdot_bh, 1);
1605
                old_dir->i_nlink--;
1606
                new_dir->i_nlink++;
1607
                /* no need to mark them dirty */
1608
                dotdot_inode->i_nlink = new_dir->i_nlink;
1609
                iput(dotdot_inode);
1610
                fat_brelse(sb, dotdot_bh);
1611
        }
1612
 
1613
        if (res > 0) res = 0;
1614
 
1615
rename_done:
1616
        if (locked)
1617
                fat_unlock_creation();
1618
        if (old_bh)
1619
                fat_brelse(sb, old_bh);
1620
        if (new_bh)
1621
                fat_brelse(sb, new_bh);
1622
        if (old_inode)
1623
                iput(old_inode);
1624
        iput(old_dir);
1625
        iput(new_dir);
1626
        return res;
1627
}
1628
 
1629
 
1630
 
1631
/* Public inode operations for the VFAT fs */
1632
struct inode_operations vfat_dir_inode_operations = {
1633
        &fat_dir_operations,    /* default directory file-ops */
1634
        vfat_create,            /* create */
1635
        vfat_lookup,            /* lookup */
1636
        NULL,                   /* link */
1637
        vfat_unlink,            /* unlink */
1638
        NULL,                   /* symlink */
1639
        vfat_mkdir,             /* mkdir */
1640
        vfat_rmdir,             /* rmdir */
1641
        NULL,                   /* mknod */
1642
        vfat_rename,            /* rename */
1643
        NULL,                   /* readlink */
1644
        NULL,                   /* follow_link */
1645
        NULL,                   /* readpage */
1646
        NULL,                   /* writepage */
1647
        fat_bmap,               /* bmap */
1648
        NULL,                   /* truncate */
1649
        NULL                    /* permission */
1650
};
1651
 
1652
 
1653
void vfat_read_inode(struct inode *inode)
1654
{
1655
        fat_read_inode(inode, &vfat_dir_inode_operations);
1656
}
1657
 
1658
#ifdef MODULE
1659
int init_module(void)
1660
{
1661
        return init_vfat_fs();
1662
}
1663
 
1664
 
1665
void cleanup_module(void)
1666
{
1667
        unregister_filesystem(&vfat_fs_type);
1668
}
1669
 
1670
#endif /* ifdef MODULE */
1671
 
1672
 
1673
 
1674
/*
1675
 * Overrides for Emacs so that we follow Linus's tabbing style.
1676
 * Emacs will notice this stuff at the end of the file and automatically
1677
 * adjust the settings for this buffer only.  This must remain at the end
1678
 * of the file.
1679
 * ---------------------------------------------------------------------------
1680
 * Local variables:
1681
 * c-indent-level: 8
1682
 * c-brace-imaginary-offset: 0
1683
 * c-brace-offset: -8
1684
 * c-argdecl-indent: 8
1685
 * c-label-offset: -8
1686
 * c-continued-statement-offset: 8
1687
 * c-continued-brace-offset: 0
1688
 * End:
1689
 */

powered by: WebSVN 2.1.0

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