root/fs/msdos/misc.c

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

DEFINITIONS

This source file includes following definitions.
  1. is_binary
  2. lock_creation
  3. unlock_creation
  4. lock_fat
  5. unlock_fat
  6. msdos_add_cluster
  7. date_dos2unix
  8. date_unix2dos
  9. msdos_get_entry
  10. msdos_scan
  11. raw_found
  12. raw_scan_root
  13. raw_scan_nonroot
  14. raw_scan
  15. msdos_parent_ino
  16. msdos_subdirs

   1 /*
   2  *  linux/fs/msdos/misc.c
   3  *
   4  *  Written 1992 by Werner Almesberger
   5  */
   6 
   7 #include <linux/msdos_fs.h>
   8 #include <linux/sched.h>
   9 #include <linux/kernel.h>
  10 #include <linux/errno.h>
  11 #include <linux/string.h>
  12 #include <linux/stat.h>
  13 
  14 static char bin_extensions[] =
  15   "EXECOMAPPSYSOVLOBJLIB"               /* program code */
  16   "ARCZIPLHALZHZOOTARZ  ARJTZ "         /* common archivers */
  17   "GIFBMPTIFGL JPGPCX"                  /* graphics */
  18   "TFMVF GF PK PXLDVI";                 /* TeX */
  19 
  20 
  21 /* Select binary/text conversion */
  22 
  23 int is_binary(char conversion,char *extension)
     /* [previous][next][first][last][top][bottom][index][help] */
  24 {
  25         char *walk;
  26 
  27         switch (conversion) {
  28                 case 'b':
  29                         return 1;
  30                 case 't':
  31                         return 0;
  32                 case 'a':
  33                         for (walk = bin_extensions; *walk; walk += 3)
  34                                 if (!strncmp(extension,walk,3)) return 1;
  35                         return 0;
  36                 default:
  37                         printk("Invalid conversion mode - defaulting to "
  38                             "binary.\n");
  39                         return 1;
  40         }
  41 }
  42 
  43 
  44 /* File creation lock. This is system-wide to avoid deadlocks in rename. */
  45 /* (rename might deadlock before detecting cross-FS moves.) */
  46 
  47 static struct wait_queue *creation_wait = NULL;
  48 static creation_lock = 0;
  49 
  50 
  51 void lock_creation(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  52 {
  53         while (creation_lock) sleep_on(&creation_wait);
  54         creation_lock = 1;
  55 }
  56 
  57 
  58 void unlock_creation(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  59 {
  60         creation_lock = 0;
  61         wake_up(&creation_wait);
  62 }
  63 
  64 
  65 void lock_fat(struct super_block *sb)
     /* [previous][next][first][last][top][bottom][index][help] */
  66 {
  67         while (MSDOS_SB(sb)->fat_lock) sleep_on(&MSDOS_SB(sb)->fat_wait);
  68         MSDOS_SB(sb)->fat_lock = 1;
  69 }
  70 
  71 
  72 void unlock_fat(struct super_block *sb)
     /* [previous][next][first][last][top][bottom][index][help] */
  73 {
  74         MSDOS_SB(sb)->fat_lock = 0;
  75         wake_up(&MSDOS_SB(sb)->fat_wait);
  76 }
  77 
  78 
  79 int msdos_add_cluster(struct inode *inode)
     /* [previous][next][first][last][top][bottom][index][help] */
  80 {
  81         static struct wait_queue *wait = NULL;
  82         static int lock = 0;
  83         static int previous = 0; /* works best if one FS is being used */
  84         int count,this,limit,last,current,sector;
  85         void *data;
  86         struct buffer_head *bh;
  87 
  88         if (inode->i_ino == MSDOS_ROOT_INO) return -ENOSPC;
  89         if (!MSDOS_SB(inode->i_sb)->free_clusters) return -ENOSPC;
  90         while (lock) sleep_on(&wait);
  91         lock = 1;
  92         lock_fat(inode->i_sb);
  93         limit = MSDOS_SB(inode->i_sb)->clusters;
  94         this = limit; /* to keep GCC happy */
  95         for (count = 0; count < limit; count++) {
  96                 this = ((count+previous) % limit)+2;
  97                 if (fat_access(inode->i_sb,this,-1) == 0) break;
  98         }
  99 #ifdef DEBUG
 100 printk("free cluster: %d\n",this);
 101 #endif
 102         previous = (count+previous+1) % limit;
 103         if (count >= limit) {
 104                 MSDOS_SB(inode->i_sb)->free_clusters = 0;
 105                 unlock_fat(inode->i_sb);
 106                 lock = 0;
 107                 wake_up(&wait);
 108                 return -ENOSPC;
 109         }
 110         fat_access(inode->i_sb,this,MSDOS_SB(inode->i_sb)->fat_bits == 12 ?
 111             0xff8 : 0xfff8);
 112         if (MSDOS_SB(inode->i_sb)->free_clusters != -1)
 113                 MSDOS_SB(inode->i_sb)->free_clusters--;
 114         unlock_fat(inode->i_sb);
 115         lock = 0;
 116         wake_up(&wait);
 117 #ifdef DEBUG
 118 printk("set to %x\n",fat_access(inode->i_sb,this,-1));
 119 #endif
 120         last = 0;
 121         if ((current = MSDOS_I(inode)->i_start) != 0) {
 122                 cache_lookup(inode,0x7fffffff,&last,&current);
 123                 while (current && current != -1)
 124                         if (!(current = fat_access(inode->i_sb,
 125                             last = current,-1)))
 126                                 panic("File without EOF");
 127         }
 128 #ifdef DEBUG
 129 printk("last = %d\n",last);
 130 #endif
 131         if (last) fat_access(inode->i_sb,last,this);
 132         else {
 133                 MSDOS_I(inode)->i_start = this;
 134                 inode->i_dirt = 1;
 135         }
 136 #ifdef DEBUG
 137 if (last) printk("next set to %d\n",fat_access(inode->i_sb,last,-1));
 138 #endif
 139         for (current = 0; current < MSDOS_SB(inode->i_sb)->cluster_size;
 140             current++) {
 141                 sector = MSDOS_SB(inode->i_sb)->data_start+(this-2)*
 142                     MSDOS_SB(inode->i_sb)->cluster_size+current;
 143 #ifdef DEBUG
 144 printk("zeroing sector %d\n",sector);
 145 #endif
 146                 if (current < MSDOS_SB(inode->i_sb)->cluster_size-1 &&
 147                     !(sector & 1)) {
 148                         if (!(bh = getblk(inode->i_dev,sector >> 1, BLOCK_SIZE)))
 149                                 printk("getblk failed\n");
 150                         else {
 151                                 memset(bh->b_data,0,BLOCK_SIZE);
 152                                 bh->b_uptodate = 1;
 153                         }
 154                         current++;
 155                 }
 156                 else {
 157                         if (!(bh = msdos_sread(inode->i_dev,sector,&data)))
 158                                 printk("msdos_sread failed\n");
 159                         else memset(data,0,SECTOR_SIZE);
 160                 }
 161                 if (bh) {
 162                         bh->b_dirt = 1;
 163                         brelse(bh);
 164                 }
 165         }
 166         inode->i_blocks += MSDOS_SB(inode->i_sb)->cluster_size;
 167         if (S_ISDIR(inode->i_mode)) {
 168                 if (inode->i_size & (SECTOR_SIZE-1))
 169                         panic("Odd directory size");
 170                 inode->i_size += SECTOR_SIZE*MSDOS_SB(inode->i_sb)->
 171                     cluster_size;
 172 #ifdef DEBUG
 173 printk("size is %d now (%x)\n",inode->i_size,inode);
 174 #endif
 175                 inode->i_dirt = 1;
 176         }
 177         return 0;
 178 }
 179 
 180 
 181 /* Linear day numbers of the respective 1sts in non-leap years. */
 182 
 183 static int day_n[] = { 0,31,59,90,120,151,181,212,243,273,304,334,0,0,0,0 };
 184                   /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */
 185 
 186 
 187 extern struct timezone sys_tz;
 188 
 189 
 190 /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
 191 
 192 int date_dos2unix(unsigned short time,unsigned short date)
     /* [previous][next][first][last][top][bottom][index][help] */
 193 {
 194         int month,year,secs;
 195 
 196         month = ((date >> 5) & 15)-1;
 197         year = date >> 9;
 198         secs = (time & 31)*2+60*((time >> 5) & 63)+(time >> 11)*3600+86400*
 199             ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 &&
 200             month < 2 ? 1 : 0)+3653);
 201                         /* days since 1.1.70 plus 80's leap day */
 202         secs += sys_tz.tz_minuteswest*60;
 203         return secs;
 204 }
 205 
 206 
 207 /* Convert linear UNIX date to a MS-DOS time/date pair. */
 208 
 209 void date_unix2dos(int unix_date,unsigned short *time,
     /* [previous][next][first][last][top][bottom][index][help] */
 210     unsigned short *date)
 211 {
 212         int day,year,nl_day,month;
 213 
 214         unix_date -= sys_tz.tz_minuteswest*60;
 215         *time = (unix_date % 60)/2+(((unix_date/60) % 60) << 5)+
 216             (((unix_date/3600) % 24) << 11);
 217         day = unix_date/86400-3652;
 218         year = day/365;
 219         if ((year+3)/4+365*year > day) year--;
 220         day -= (year+3)/4+365*year;
 221         if (day == 59 && !(year & 3)) {
 222                 nl_day = day;
 223                 month = 2;
 224         }
 225         else {
 226                 nl_day = (year & 3) || day <= 59 ? day : day-1;
 227                 for (month = 0; month < 12; month++)
 228                         if (day_n[month] > nl_day) break;
 229         }
 230         *date = nl_day-day_n[month-1]+1+(month << 5)+(year << 9);
 231 }
 232 
 233 
 234 /* Returns the inode number of the directory entry at offset pos. If bh is
 235    non-NULL, it is brelse'd before. Pos is incremented. The buffer header is
 236    returned in bh. */
 237 
 238 int msdos_get_entry(struct inode *dir,int *pos,struct buffer_head **bh,
     /* [previous][next][first][last][top][bottom][index][help] */
 239     struct msdos_dir_entry **de)
 240 {
 241         int sector,offset;
 242         void *data;
 243 
 244         while (1) {
 245                 offset = *pos;
 246                 if ((sector = msdos_smap(dir,offset >> SECTOR_BITS)) == -1)
 247                         return -1;
 248                 if (!sector)
 249                         return -1; /* FAT error ... */
 250                 *pos += sizeof(struct msdos_dir_entry);
 251                 if (*bh)
 252                         brelse(*bh);
 253                 if (!(*bh = msdos_sread(dir->i_dev,sector,&data))) {
 254                         printk("Directory sread (sector %d) failed\n",sector);
 255                         continue;
 256                 }
 257                 *de = (struct msdos_dir_entry *) (data+(offset &
 258                     (SECTOR_SIZE-1)));
 259                 return (sector << MSDOS_DPS_BITS)+((offset & (SECTOR_SIZE-1)) >>
 260                     MSDOS_DIR_BITS);
 261         }
 262 }
 263 
 264 
 265 /* Scans a directory for a given file (name points to its formatted name) or
 266    for an empty directory slot (name is NULL). Returns the inode number. */
 267 
 268 int msdos_scan(struct inode *dir,char *name,struct buffer_head **res_bh,
     /* [previous][next][first][last][top][bottom][index][help] */
 269     struct msdos_dir_entry **res_de,int *ino)
 270 {
 271         int pos;
 272         struct msdos_dir_entry *de;
 273         struct inode *inode;
 274 
 275         pos = 0;
 276         *res_bh = NULL;
 277         while ((*ino = msdos_get_entry(dir,&pos,res_bh,&de)) > -1) {
 278                 if (name) {
 279                         if (de->name[0] && ((unsigned char *) (de->name))[0]
 280                             != DELETED_FLAG && !(de->attr & ATTR_VOLUME) &&
 281                             !strncmp(de->name,name,MSDOS_NAME)) break;
 282                 }
 283                 else if (!de->name[0] || ((unsigned char *) (de->name))[0] ==
 284                             DELETED_FLAG) {
 285                                 if (!(inode = iget(dir->i_sb,*ino))) break;
 286                                 if (!MSDOS_I(inode)->i_busy) {
 287                                         iput(inode);
 288                                         break;
 289                                 }
 290         /* skip deleted files that haven't been closed yet */
 291                                 iput(inode);
 292                         }
 293         }
 294         if (*ino == -1) {
 295                 if (*res_bh) brelse(*res_bh);
 296                 *res_bh = NULL;
 297                 return name ? -ENOENT : -ENOSPC;
 298         }
 299         *res_de = de;
 300         return 0;
 301 }
 302 
 303 
 304 /* Now an ugly part: this set of directory scan routines works on clusters
 305    rather than on inodes and sectors. They are necessary to locate the '..'
 306    directory "inode". raw_found operates in three modes: if name is non-NULL,
 307    the directory is scanned for an entry with that name. If ino is non-NULL,
 308    the directory is scanned for an entry whose data starts at *number. If name
 309    and ino are NULL, the directory entries are counted in *number. */
 310 
 311 
 312 static int raw_found(struct super_block *sb,int sector,char *name,int *number,
     /* [previous][next][first][last][top][bottom][index][help] */
 313     int *ino)
 314 {
 315         struct buffer_head *bh;
 316         struct msdos_dir_entry *data;
 317         int entry,start,done;
 318 
 319         if (!(bh = msdos_sread(sb->s_dev,sector,(void **) &data))) return -EIO;
 320         for (entry = 0; entry < MSDOS_DPS; entry++) {
 321                 if (name) done = !strncmp(data[entry].name,name,MSDOS_NAME);
 322                 else {
 323                         if (ino)
 324                                 done = *(unsigned char *) data[entry].name !=
 325                                     DELETED_FLAG && data[entry].start ==
 326                                     *number;
 327                         else {
 328                                 done = 0;
 329                                 if (*data[entry].name && *(unsigned char *)
 330                                     data[entry].name != DELETED_FLAG &&
 331                                     (data[entry].attr & ATTR_DIR)) (*number)++;
 332                         }
 333                 }
 334                 if (done) {
 335                         if (ino) *ino = sector*MSDOS_DPS+entry;
 336                         start = data[entry].start;
 337                         brelse(bh);
 338                         return start;
 339                 }
 340         }
 341         brelse(bh);
 342         return -1;
 343 }
 344 
 345 
 346 static int raw_scan_root(struct super_block *sb,char *name,int *number,int *ino)
     /* [previous][next][first][last][top][bottom][index][help] */
 347 {
 348         int count,cluster;
 349 
 350         for (count = 0; count < MSDOS_SB(sb)->dir_entries/MSDOS_DPS; count++) {
 351                 if ((cluster = raw_found(sb,MSDOS_SB(sb)->dir_start+count,name,
 352                     number,ino)) >= 0) return cluster;
 353         }
 354         return -ENOENT;
 355 }
 356 
 357 
 358 static int raw_scan_nonroot(struct super_block *sb,int start,char *name,
     /* [previous][next][first][last][top][bottom][index][help] */
 359     int *number,int *ino)
 360 {
 361         int count,cluster;
 362 
 363 #ifdef DEBUG
 364         printk("raw_scan_nonroot: start=%d\n",start);
 365 #endif
 366         do {
 367                 for (count = 0; count < MSDOS_SB(sb)->cluster_size; count++) {
 368                         if ((cluster = raw_found(sb,(start-2)*MSDOS_SB(sb)->
 369                             cluster_size+MSDOS_SB(sb)->data_start+count,name,
 370                             number,ino)) >= 0) return cluster;
 371                 }
 372                 if (!(start = fat_access(sb,start,-1))) panic("FAT error");
 373 #ifdef DEBUG
 374         printk("next start: %d\n",start);
 375 #endif
 376         }
 377         while (start != -1);
 378         return -ENOENT;
 379 }
 380 
 381 
 382 static int raw_scan(struct super_block *sb,int start,char *name,int number,
     /* [previous][next][first][last][top][bottom][index][help] */
 383     int *ino)
 384 {
 385     if (start) return raw_scan_nonroot(sb,start,name,&number,ino);
 386     else return raw_scan_root(sb,name,&number,ino);
 387 }
 388 
 389 
 390 int msdos_parent_ino(struct inode *dir,int locked)
     /* [previous][next][first][last][top][bottom][index][help] */
 391 {
 392         int error,current,prev,this;
 393 
 394         if (!S_ISDIR(dir->i_mode)) panic("Non-directory fed to m_p_i");
 395         if (dir->i_ino == MSDOS_ROOT_INO) return dir->i_ino;
 396         if (!locked) lock_creation(); /* prevent renames */
 397         if ((current = raw_scan(dir->i_sb,MSDOS_I(dir)->i_start,MSDOS_DOTDOT,0,
 398             NULL)) < 0) {
 399                 if (!locked) unlock_creation();
 400                 return current;
 401         }
 402         if (!current) this = MSDOS_ROOT_INO;
 403         else {
 404                 if ((prev = raw_scan(dir->i_sb,current,MSDOS_DOTDOT,0,NULL)) <
 405                     0) {
 406                         if (!locked) unlock_creation();
 407                         return prev;
 408                 }
 409                 if ((error = raw_scan(dir->i_sb,prev,NULL,current,&this)) < 0) {
 410                         if (!locked) unlock_creation();
 411                         return error;
 412                 }
 413         }
 414         if (!locked) unlock_creation();
 415         return this;
 416 }
 417 
 418 
 419 int msdos_subdirs(struct inode *dir)
     /* [previous][next][first][last][top][bottom][index][help] */
 420 {
 421         int count;
 422 
 423         count = 0;
 424         if (dir->i_ino == MSDOS_ROOT_INO)
 425                 (void) raw_scan_root(dir->i_sb,NULL,&count,NULL);
 426         else {
 427                 if (!MSDOS_I(dir)->i_start) return 0; /* in mkdir */
 428                 else (void) raw_scan_nonroot(dir->i_sb,MSDOS_I(dir)->i_start,
 429                     NULL,&count,NULL);
 430         }
 431         return count;
 432 }

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