root/fs/vfat/namei.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. vfat_put_super
  2. parse_options
  3. vfat_read_super
  4. check_stack
  5. dump_fat
  6. dump_de
  7. vfat_valid_longname
  8. vfat_valid_shortname
  9. vfat_format_name
  10. vfat_create_shortname
  11. vfat_find_free_slots
  12. xlate_to_uni
  13. vfat_fill_long_slots
  14. vfat_build_slots
  15. vfat_readdir_cb
  16. vfat_find
  17. vfat_lookup
  18. vfat_create_entry
  19. vfat_create
  20. vfat_create_a_dotdir
  21. vfat_create_dotdirs
  22. vfat_empty
  23. vfat_rmdir_free_ino
  24. vfat_unlink_free_ino
  25. vfat_remove_entry
  26. vfat_rmdirx
  27. vfat_rmdir
  28. vfat_unlinkx
  29. vfat_mkdir
  30. vfat_unlink
  31. vfat_rename
  32. vfat_read_inode
  33. init_vfat_fs
  34. init_module
  35. cleanup_module

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

/* [previous][next][first][last][top][bottom][index][help] */