root/fs/fat/dir.c

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

DEFINITIONS

This source file includes following definitions.
  1. fat_dir_read
  2. uni2ascii
  3. fat_readdirx
  4. fat_filldir
  5. fat_readdir
  6. vfat_ioctl_fill
  7. fat_dir_ioctl

   1 /*
   2  *  linux/fs/fat/dir.c
   3  *
   4  *  directory handling functions for fat-based filesystems
   5  *
   6  *  Written 1992,1993 by Werner Almesberger
   7  *
   8  *  Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu>
   9  *
  10  *  VFAT extensions by Gordon Chaffee <chaffee@plateau.cs.berkeley.edu>
  11  *  Merged with msdos fs by Henrik Storner <storner@osiris.ping.dk>
  12  */
  13 
  14 #include <linux/fs.h>
  15 #include <linux/msdos_fs.h>
  16 #include <linux/kernel.h>
  17 #include <linux/errno.h>
  18 #include <linux/stat.h>
  19 #include <linux/string.h>
  20 #include <linux/ioctl.h>
  21 #include <linux/dirent.h>
  22 #include <linux/mm.h>
  23 
  24 #include <asm/segment.h>
  25 
  26 #include "msbuffer.h"
  27 #include "tables.h"
  28 
  29 
  30 #define PRINTK(X)
  31 
  32 static int fat_dir_read(struct inode * inode,struct file * filp, char * buf,int count)
     /* [previous][next][first][last][top][bottom][index][help] */
  33 {
  34         return -EISDIR;
  35 }
  36 
  37 struct file_operations fat_dir_operations = {
  38         NULL,                   /* lseek - default */
  39         fat_dir_read,           /* read */
  40         NULL,                   /* write - bad */
  41         fat_readdir,            /* readdir */
  42         NULL,                   /* select - default */
  43         fat_dir_ioctl,          /* ioctl - default */
  44         NULL,                   /* mmap */
  45         NULL,                   /* no special open code */
  46         NULL,                   /* no special release code */
  47         file_fsync              /* fsync */
  48 };
  49 
  50 /* Convert Unicode string to ASCII.  If uni_xlate is enabled and we
  51  * can't get a 1:1 conversion, use a colon as an escape character since
  52  * it is normally invalid on the vfat filesystem.  The following three
  53  * characters are a sort of uuencoded 16 bit Unicode value.  This lets
  54  * us do a full dump and restore of Unicode filenames.  We could get
  55  * into some trouble with long Unicode names, but ignore that right now.
  56  */
  57 static int
  58 uni2ascii(unsigned char *uni, unsigned char *ascii, int uni_xlate)
     /* [previous][next][first][last][top][bottom][index][help] */
  59 {
  60         unsigned char *ip, *op;
  61         unsigned char page, pg_off;
  62         unsigned char *uni_page;
  63         unsigned short val;
  64 
  65         ip = uni;
  66         op = ascii;
  67 
  68         while (*ip || ip[1]) {
  69                 pg_off = *ip++;
  70                 page = *ip++;
  71                 
  72                 uni_page = fat_uni2asc_pg[page];
  73                 if (uni_page && uni_page[pg_off]) {
  74                         *op++ = uni_page[pg_off];
  75                 } else {
  76                         if (uni_xlate == 1) {
  77                                 *op++ = ':';
  78                                 val = (pg_off << 8) + page;
  79                                 op[2] = fat_uni2code[val & 0x3f];
  80                                 val >>= 6;
  81                                 op[1] = fat_uni2code[val & 0x3f];
  82                                 val >>= 6;
  83                                 *op = fat_uni2code[val & 0x3f];
  84                                 op += 3;
  85                         } else {
  86                                 *op++ = '?';
  87                         }
  88                 }
  89         }
  90         *op = 0;
  91         return (op - ascii);
  92 }
  93 
  94 int fat_readdirx(
     /* [previous][next][first][last][top][bottom][index][help] */
  95         struct inode *inode,
  96         struct file *filp,
  97         void *dirent,
  98         fat_filldir_t fat_filldir,
  99         filldir_t filldir,
 100         int shortnames,
 101         int longnames,
 102         int both)
 103 {
 104         struct super_block *sb = inode->i_sb;
 105         int ino,i,i2,last;
 106         char c;
 107         struct buffer_head *bh;
 108         struct msdos_dir_entry *de;
 109         unsigned long oldpos = filp->f_pos;
 110         unsigned long spos;
 111         int is_long;
 112         char longname[275];
 113         unsigned char long_len = 0; /* Make compiler warning go away */
 114         unsigned char alias_checksum = 0; /* Make compiler warning go away */
 115         unsigned char long_slots = 0;
 116         int uni_xlate = MSDOS_SB(sb)->options.unicode_xlate;
 117         unsigned char *unicode = NULL;
 118 
 119         if (!inode || !S_ISDIR(inode->i_mode))
 120                 return -EBADF;
 121 /* Fake . and .. for the root directory. */
 122         if (inode->i_ino == MSDOS_ROOT_INO) {
 123                 while (oldpos < 2) {
 124                         if (fat_filldir(filldir, dirent, "..", oldpos+1, 0, oldpos, oldpos, 0, MSDOS_ROOT_INO) < 0)
 125                                 return 0;
 126                         oldpos++;
 127                         filp->f_pos++;
 128                 }
 129                 if (oldpos == 2)
 130                         filp->f_pos = 0;
 131         }
 132         if (filp->f_pos & (sizeof(struct msdos_dir_entry)-1))
 133                 return -ENOENT;
 134 
 135         bh = NULL;
 136         longname[0] = longname[1] = 0;
 137         is_long = 0;
 138         ino = fat_get_entry(inode,&filp->f_pos,&bh,&de);
 139         while (ino > -1) {
 140                 /* Check for long filename entry */
 141                 if (MSDOS_SB(sb)->options.isvfat && (de->name[0] == (__s8) DELETED_FLAG)) {
 142                         is_long = 0;
 143                         oldpos = filp->f_pos;
 144                 } else if (MSDOS_SB(sb)->options.isvfat && de->attr ==  ATTR_EXT) {
 145                         int get_new_entry;
 146                         struct msdos_dir_slot *ds;
 147                         int offset;
 148                         unsigned char id;
 149                         unsigned char slot;
 150                         unsigned char slots = 0;
 151 
 152                         if (!unicode) {
 153                                 unicode = (unsigned char *)
 154                                         __get_free_page(GFP_KERNEL);
 155                                 if (!unicode)
 156                                         return -ENOMEM;
 157                         }
 158 
 159                         offset = 0;
 160                         ds = (struct msdos_dir_slot *) de;
 161                         id = ds->id;
 162                         if (id & 0x40) {
 163                                 slots = id & ~0x40;
 164                                 long_slots = slots;
 165                                 is_long = 1;
 166                                 alias_checksum = ds->alias_checksum;
 167                         }
 168 
 169                         get_new_entry = 1;
 170                         slot = slots;
 171                         while (slot > 0) {
 172                                 PRINTK(("1. get_new_entry: %d\n", get_new_entry));
 173                                 if (ds->attr !=  ATTR_EXT) {
 174                                         is_long = 0;
 175                                         get_new_entry = 0;
 176                                         break;
 177                                 }
 178                                 if ((ds->id & ~0x40) != slot) {
 179                                         is_long = 0;
 180                                         break;
 181                                 }
 182                                 if (ds->alias_checksum != alias_checksum) {
 183                                         is_long = 0;
 184                                         break;
 185                                 }
 186                                 slot--;
 187                                 offset = slot * 26;
 188                                 PRINTK(("2. get_new_entry: %d\n", get_new_entry));
 189                                 memcpy(&unicode[offset], ds->name0_4, 10);
 190                                 offset += 10;
 191                                 memcpy(&unicode[offset], ds->name5_10, 12);
 192                                 offset += 12;
 193                                 memcpy(&unicode[offset], ds->name11_12, 4);
 194                                 offset += 4;
 195 
 196                                 if (ds->id & 0x40) {
 197                                         unicode[offset] = 0;
 198                                         unicode[offset+1] = 0;
 199                                 }
 200                                 if (slot > 0) {
 201                                         ino = fat_get_entry(inode,&filp->f_pos,&bh,&de);
 202                                         PRINTK(("4. get_new_entry: %d\n", get_new_entry));
 203                                         if (ino == -1) {
 204                                                 is_long = 0;
 205                                                 get_new_entry = 0;
 206                                                 break;
 207                                         }
 208                                         ds = (struct msdos_dir_slot *) de;
 209                                 }
 210                                 PRINTK(("5. get_new_entry: %d\n", get_new_entry));
 211                         }
 212                 } else if (!IS_FREE(de->name) && !(de->attr & ATTR_VOLUME)) {
 213                         char bufname[14];
 214                         char *ptname = bufname;
 215                         int dotoffset = 0;
 216 
 217                         if (is_long) {
 218                                 unsigned char sum;
 219                                 long_len = uni2ascii(unicode, longname, uni_xlate);
 220                                 for (sum = 0, i = 0; i < 11; i++) {
 221                                         sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i];
 222                                 }
 223 
 224                                 if (sum != alias_checksum) {
 225                                         PRINTK(("Checksums don't match %d != %d\n", sum, alias_checksum));
 226                                         is_long = 0;
 227                                 }
 228                         }
 229 
 230                         if ((de->attr & ATTR_HIDDEN) && MSDOS_SB(sb)->options.dotsOK) {
 231                                 bufname[0] = '.';
 232                                 dotoffset = 1;
 233                                 ptname = bufname+1;
 234                         }
 235                         for (i = 0, last = 0; i < 8; i++) {
 236                                 if (!(c = de->name[i])) break;
 237                                 if (c >= 'A' && c <= 'Z') c += 32;
 238                                 /* see namei.c, msdos_format_name */
 239                                 if (c == 0x05) c = 0xE5;
 240                                 if (c != ' ')
 241                                         last = i+1;
 242                                 ptname[i] = c;
 243                         }
 244                         i = last;
 245                         ptname[i] = '.';
 246                         i++;
 247                         for (i2 = 0; i2 < 3; i2++) {
 248                                 if (!(c = de->ext[i2])) break;
 249                                 if (c >= 'A' && c <= 'Z') c += 32;
 250                                 if (c != ' ')
 251                                         last = i+1;
 252                                 ptname[i] = c;
 253                                 i++;
 254                         }
 255                         if ((i = last) != 0) {
 256                                 if (!strcmp(de->name,MSDOS_DOT))
 257                                         ino = inode->i_ino;
 258                                 else if (!strcmp(de->name,MSDOS_DOTDOT))
 259                                         ino = fat_parent_ino(inode,0);
 260 
 261                                 if (shortnames || !is_long) {
 262                                         dcache_add(inode, bufname, i+dotoffset, ino);
 263                                         if (both) {
 264                                                 bufname[i+dotoffset] = '\0';
 265                                         }
 266                                         spos = oldpos;
 267                                         if (is_long) {
 268                                                 spos = filp->f_pos - sizeof(struct msdos_dir_entry);
 269                                         } else {
 270                                                 long_slots = 0;
 271                                         }
 272                                         if (fat_filldir(filldir, dirent, bufname, i+dotoffset, 0, oldpos, spos, long_slots, ino) < 0) {
 273                                                 filp->f_pos = oldpos;
 274                                                 break;
 275                                         }
 276                                 }
 277                                 if (is_long && longnames) {
 278                                         dcache_add(inode, longname, long_len, ino);
 279                                         if (both) {
 280                                                 memcpy(&longname[long_len+1], bufname, i+dotoffset);
 281                                                 long_len += i+dotoffset;
 282                                         }
 283                                         spos = filp->f_pos - sizeof(struct msdos_dir_entry);
 284                                         if (fat_filldir(filldir, dirent, longname, long_len, 1, oldpos, spos, long_slots, ino) < 0) {
 285                                                 filp->f_pos = oldpos;
 286                                                 break;
 287                                         }
 288                                 }
 289                                 oldpos = filp->f_pos;
 290                         }
 291                         is_long = 0;
 292                 } else {
 293                         is_long = 0;
 294                         oldpos = filp->f_pos;
 295                 }
 296                 ino = fat_get_entry(inode,&filp->f_pos,&bh,&de);        
 297         }
 298         if (bh) brelse(bh);
 299         if (unicode) {
 300                 free_page((unsigned long) unicode);
 301         }
 302         return 0;
 303 }
 304 
 305 static int fat_filldir(
     /* [previous][next][first][last][top][bottom][index][help] */
 306         filldir_t filldir,
 307         void * buf,
 308         const char * name,
 309         int name_len,
 310         int is_long,
 311         off_t offset,
 312         off_t short_offset,
 313         int long_slots,
 314         ino_t ino)
 315 {
 316         return filldir(buf, name, name_len, offset, ino);
 317 }
 318 
 319 int fat_readdir(
     /* [previous][next][first][last][top][bottom][index][help] */
 320         struct inode *inode,
 321         struct file *filp,
 322         void *dirent,
 323         filldir_t filldir)
 324 {
 325         return fat_readdirx(inode, filp, dirent, fat_filldir, filldir,
 326                             0, 1, 0);
 327 }
 328 
 329 static int vfat_ioctl_fill(
     /* [previous][next][first][last][top][bottom][index][help] */
 330         filldir_t filldir,
 331         void * buf,
 332         const char * name,
 333         int name_len,
 334         int is_long,
 335         off_t offset,
 336         off_t short_offset,
 337         int long_slots,
 338         ino_t ino)
 339 {
 340         struct dirent *d1 = (struct dirent *)buf;
 341         struct dirent *d2 = d1 + 1;
 342         int len, slen;
 343         int dotdir;
 344 
 345         if (get_user(&d1->d_reclen) != 0) {
 346                 return -1;
 347         }
 348 
 349         if ((name_len == 1 && name[0] == '.') ||
 350             (name_len == 2 && name[0] == '.' && name[1] == '.')) {
 351                 dotdir = 1;
 352                 len = name_len;
 353         } else {
 354                 dotdir = 0;
 355                 len = strlen(name);
 356         }
 357         if (len != name_len) {
 358                 memcpy_tofs(d2->d_name, name, len);
 359                 put_user(0, d2->d_name + len);
 360                 put_user(len, &d2->d_reclen);
 361                 put_user(ino, &d2->d_ino);
 362                 put_user(offset, &d2->d_off);
 363                 slen = name_len - len;
 364                 memcpy_tofs(d1->d_name, name+len+1, slen);
 365                 put_user(0, d1->d_name+slen);
 366                 put_user(slen, &d1->d_reclen);
 367         } else {
 368                 put_user(0, d2->d_name);
 369                 put_user(0, &d2->d_reclen);
 370                 memcpy_tofs(d1->d_name, name, len);
 371                 put_user(0, d1->d_name+len);
 372                 put_user(len, &d1->d_reclen);
 373         }
 374         PRINTK(("FAT d1=%p d2=%p len=%d, name_len=%d\n",
 375                 d1, d2, len, name_len));
 376 
 377         return 0;
 378 }
 379 
 380 int fat_dir_ioctl(struct inode * inode, struct file * filp,
     /* [previous][next][first][last][top][bottom][index][help] */
 381                   unsigned int cmd, unsigned long arg)
 382 {
 383         /*
 384          * We want to provide an interface for Samba to be able
 385          * to get the short filename for a given long filename.
 386          * Samba should use this ioctl instead of readdir() to
 387          * get the information it needs.
 388          */
 389         switch (cmd) {
 390         case VFAT_IOCTL_READDIR_BOTH: {
 391                 struct dirent *d1 = (struct dirent *)arg;
 392                 put_user(0, &d1->d_reclen);
 393                 return fat_readdirx(inode,filp,(void *)arg,
 394                                     vfat_ioctl_fill, NULL, 0, 1, 1);
 395         }
 396         case VFAT_IOCTL_READDIR_SHORT: {
 397                 struct dirent *d1 = (struct dirent *)arg;
 398                 put_user(0, &d1->d_reclen);
 399                 return fat_readdirx(inode,filp,(void *)arg,
 400                                     vfat_ioctl_fill, NULL, 1, 0, 1);
 401         }
 402         default:
 403                 return -EINVAL;
 404         }
 405 
 406         return 0;
 407 }
 408 
 409 /*
 410  * Overrides for Emacs so that we follow Linus's tabbing style.
 411  * Emacs will notice this stuff at the end of the file and automatically
 412  * adjust the settings for this buffer only.  This must remain at the end
 413  * of the file.
 414  * ---------------------------------------------------------------------------
 415  * Local variables:
 416  * c-indent-level: 8
 417  * c-brace-imaginary-offset: 0
 418  * c-brace-offset: -8
 419  * c-argdecl-indent: 8
 420  * c-label-offset: -8
 421  * c-continued-statement-offset: 8
 422  * c-continued-brace-offset: 0
 423  * End:
 424  */

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