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. msdos_add_cluster
  5. date_dos2unix
  6. date_unix2dos
  7. msdos_get_entry
  8. msdos_scan
  9. raw_found
  10. raw_scan_root
  11. raw_scan_nonroot
  12. raw_scan
  13. msdos_parent_ino

   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                         panic("Invalid conversion mode");
  38         }
  39 }
  40 
  41 
  42 static struct wait_queue *creation_wait = NULL;
  43 static creation_lock = 0;
  44 
  45 
  46 void lock_creation(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  47 {
  48         while (creation_lock) sleep_on(&creation_wait);
  49         creation_lock = 1;
  50 }
  51 
  52 
  53 void unlock_creation(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  54 {
  55         creation_lock = 0;
  56         wake_up(&creation_wait);
  57 }
  58 
  59 
  60 int msdos_add_cluster(struct inode *inode)
     /* [previous][next][first][last][top][bottom][index][help] */
  61 {
  62         static struct wait_queue *wait = NULL;
  63         static int lock = 0;
  64         static int previous = 0; /* works best if one FS is being used */
  65         int count,this,limit,last,current,sector;
  66         void *data;
  67         struct buffer_head *bh;
  68 
  69         if (inode->i_ino == MSDOS_ROOT_INO) return -ENOSPC;
  70         while (lock) sleep_on(&wait);
  71         lock = 1;
  72         limit = MSDOS_SB(inode->i_sb)->clusters;
  73         this = limit; /* to keep GCC happy */
  74         for (count = 0; count < limit; count++) {
  75                 this = ((count+previous) % limit)+2;
  76                 if (fat_access(inode->i_sb,this,-1) == 0) break;
  77         }
  78 #ifdef DEBUG
  79 printk("free cluster: %d\r\n",this);
  80 #endif
  81         previous = (count+previous+1) % limit;
  82         if (count >= limit) {
  83                 lock = 0;
  84                 wake_up(&wait);
  85                 return -ENOSPC;
  86         }
  87         fat_access(inode->i_sb,this,MSDOS_SB(inode->i_sb)->fat_bits == 12 ?
  88             0xff8 : 0xfff8);
  89         lock = 0;
  90         wake_up(&wait);
  91 #ifdef DEBUG
  92 printk("set to %x\r\n",fat_access(inode->i_sb,this,-1));
  93 #endif
  94         if (!S_ISDIR(inode->i_mode)) {
  95                 last = inode->i_size ? get_cluster(inode,(inode->i_size-1)/
  96                     SECTOR_SIZE/MSDOS_SB(inode->i_sb)->cluster_size) : 0;
  97         }
  98         else {
  99                 last = 0;
 100                 if (current = inode->i_data[D_START]) {
 101                         cache_lookup(inode,0x7fffffff,&last,&current);
 102                         while (current && current != -1)
 103                                 if (!(current = fat_access(inode->i_sb,
 104                                     last = current,-1)))
 105                                         panic("File without EOF");
 106                         }
 107         }
 108 #ifdef DEBUG
 109 printk("last = %d\r\n",last);
 110 #endif
 111         if (last) fat_access(inode->i_sb,last,this);
 112         else {
 113                 inode->i_data[D_START] = this;
 114                 inode->i_dirt = 1;
 115         }
 116 #ifdef DEBUG
 117 if (last) printk("next set to %d\r\n",fat_access(inode->i_sb,last,-1));
 118 #endif
 119         for (current = 0; current < MSDOS_SB(inode->i_sb)->cluster_size;
 120             current++) {
 121                 sector = MSDOS_SB(inode->i_sb)->data_start+(this-2)*
 122                     MSDOS_SB(inode->i_sb)->cluster_size+current;
 123 #ifdef DEBUG
 124 printk("zeroing sector %d\r\n",sector);
 125 #endif
 126                 if (current < MSDOS_SB(inode->i_sb)->cluster_size-1 &&
 127                     !(sector & 1)) {
 128                         if (!(bh = getblk(inode->i_dev,sector >> 1, BLOCK_SIZE)))
 129                                 printk("getblk failed\r\n");
 130                         else {
 131                                 memset(bh->b_data,0,BLOCK_SIZE);
 132                                 bh->b_uptodate = 1;
 133                         }
 134                         current++;
 135                 }
 136                 else {
 137                         if (!(bh = msdos_sread(inode->i_dev,sector,&data)))
 138                                 printk("msdos_sread failed\r\n");
 139                         else memset(data,0,SECTOR_SIZE);
 140                 }
 141                 if (bh) {
 142                         bh->b_dirt = 1;
 143                         brelse(bh);
 144                 }
 145         }
 146         if (S_ISDIR(inode->i_mode)) {
 147                 if (inode->i_size & (SECTOR_SIZE-1))
 148                         panic("Odd directory size");
 149                 inode->i_size += SECTOR_SIZE*MSDOS_SB(inode->i_sb)->
 150                     cluster_size;
 151 #ifdef DEBUG
 152 printk("size is %d now (%x)\r\n",inode->i_size,inode);
 153 #endif
 154                 inode->i_dirt = 1;
 155         }
 156         return 0;
 157 }
 158 
 159 
 160 /* Linear day numbers of the respective 1sts in non-leap years. */
 161 
 162 static int day_n[] = { 0,31,59,90,120,151,181,212,243,273,304,334,0,0,0,0 };
 163                   /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */
 164 
 165 
 166 /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
 167 
 168 int date_dos2unix(unsigned short time,unsigned short date)
     /* [previous][next][first][last][top][bottom][index][help] */
 169 {
 170         int month,year;
 171 
 172         month = ((date >> 5) & 15)-1;
 173         year = date >> 9;
 174         return (time & 31)*2+60*((time >> 5) & 63)+(time >> 11)*3600+86400*
 175             ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 &&
 176             month < 2 ? 1 : 0)+3653);
 177                         /* days since 1.1.70 plus 80's leap day */
 178 }
 179 
 180 
 181 /* Convert linear UNIX date to a MS-DOS time/date pair. */
 182 
 183 void date_unix2dos(int unix_date,unsigned short *time,
     /* [previous][next][first][last][top][bottom][index][help] */
 184     unsigned short *date)
 185 {
 186         int day,year,nl_day,month;
 187 
 188         *time = (unix_date % 60)/2+(((unix_date/60) % 60) << 5)+
 189             (((unix_date/3600) % 24) << 11);
 190         day = unix_date/86400-3652;
 191         year = day/365;
 192         if ((year+3)/4+365*year > day) year--;
 193         day -= (year+3)/4+365*year;
 194         if (day == 59 && !(year & 3)) {
 195                 nl_day = day;
 196                 month = 2;
 197         }
 198         else {
 199                 nl_day = (year & 3) || day <= 59 ? day : day-1;
 200                 for (month = 0; month < 12; month++)
 201                         if (day_n[month] > nl_day) break;
 202         }
 203         *date = nl_day-day_n[month-1]+1+(month << 5)+(year << 9);
 204 }
 205 
 206 
 207 /* Returns the inode number of the directory entry at offset pos. If bh is
 208    non-NULL, it is brelse'd before. Pos is incremented. The buffer header is
 209    returned in bh. */
 210 
 211 int msdos_get_entry(struct inode *dir,int *pos,struct buffer_head **bh,
     /* [previous][next][first][last][top][bottom][index][help] */
 212     struct msdos_dir_entry **de)
 213 {
 214         int sector,offset;
 215         void *data;
 216 
 217         while (1) {
 218                 offset = *pos;
 219                 if ((sector = msdos_smap(dir,*pos >> SECTOR_BITS)) == -1)
 220                         return -1;
 221                 if (!sector)
 222                         return -1; /* FAT error ... */
 223                 *pos += sizeof(struct msdos_dir_entry);
 224                 if (*bh)
 225                         brelse(*bh);
 226                 if (!(*bh = msdos_sread(dir->i_dev,sector,&data)))
 227                         continue;
 228                 *de = (struct msdos_dir_entry *) (data+(offset &
 229                     (SECTOR_SIZE-1)));
 230                 return (sector << MSDOS_DPS_BITS)+((offset & (SECTOR_SIZE-1)) >>
 231                     MSDOS_DIR_BITS);
 232         }
 233 }
 234 
 235 
 236 /* Scans a directory for a given file (name points to its formatted name) or
 237    for an empty directory slot (name is NULL). Returns the inode number. */
 238 
 239 int msdos_scan(struct inode *dir,char *name,struct buffer_head **res_bh,
     /* [previous][next][first][last][top][bottom][index][help] */
 240     struct msdos_dir_entry **res_de,int *ino)
 241 {
 242         int pos;
 243         struct msdos_dir_entry *de;
 244         struct inode *inode;
 245 
 246         pos = 0;
 247         *res_bh = NULL;
 248         while ((*ino = msdos_get_entry(dir,&pos,res_bh,&de)) > -1) {
 249                 if (name) {
 250                         if (de->name[0] && ((unsigned char *) (de->name))[0]
 251                             != DELETED_FLAG && !(de->attr & ATTR_VOLUME) &&
 252                             !strncmp(de->name,name,MSDOS_NAME)) break;
 253                 }
 254                 else if (!de->name[0] || ((unsigned char *) (de->name))[0] ==
 255                             DELETED_FLAG) {
 256                                 if (!(inode = iget(dir->i_dev,*ino))) break;
 257                                 if (!inode->i_data[D_BUSY]) {
 258                                         iput(inode);
 259                                         break;
 260                                 }
 261         /* skip deleted files that haven't been closed yet */
 262                                 iput(inode);
 263                         }
 264         }
 265         if (*ino == -1) {
 266                 if (*res_bh) brelse(*res_bh);
 267                 *res_bh = NULL;
 268                 return name ? -ENOENT : -ENOSPC;
 269         }
 270         *res_de = de;
 271         return 0;
 272 }
 273 
 274 
 275 /* Now an ugly part: this set of directory scan routines works on clusters
 276    rather than on inodes and sectors. They are necessary to locate the '..'
 277    directory "inode". */
 278 
 279 
 280 static int raw_found(struct super_block *sb,int sector,char *name,int number,
     /* [previous][next][first][last][top][bottom][index][help] */
 281     int *ino)
 282 {
 283         struct buffer_head *bh;
 284         struct msdos_dir_entry *data;
 285         int entry,start;
 286 
 287         if (!(bh = msdos_sread(sb->s_dev,sector,(void **) &data))) return -EIO;
 288         for (entry = 0; entry < MSDOS_DPS; entry++)
 289                 if (name ? !strncmp(data[entry].name,name,MSDOS_NAME) :
 290                     *(unsigned char *) data[entry].name != DELETED_FLAG &&
 291                     data[entry].start == number) {
 292                         if (ino) *ino = sector*MSDOS_DPS+entry;
 293                         start = data[entry].start;
 294                         brelse(bh);
 295                         return start;
 296                 }
 297         brelse(bh);
 298         return -1;
 299 }
 300 
 301 
 302 static int raw_scan_root(struct super_block *sb,char *name,int number,int *ino)
     /* [previous][next][first][last][top][bottom][index][help] */
 303 {
 304         int count,cluster;
 305 
 306         for (count = 0; count < MSDOS_SB(sb)->dir_entries/MSDOS_DPS; count++) {
 307                 if ((cluster = raw_found(sb,MSDOS_SB(sb)->dir_start+count,name,
 308                     number,ino)) >= 0) return cluster;
 309         }
 310         return -ENOENT;
 311 }
 312 
 313 
 314 static int raw_scan_nonroot(struct super_block *sb,int start,char *name,
     /* [previous][next][first][last][top][bottom][index][help] */
 315     int number,int *ino)
 316 {
 317         int count,cluster;
 318 
 319         do {
 320                 for (count = 0; count < MSDOS_SB(sb)->cluster_size; count++) {
 321                         if ((cluster = raw_found(sb,(start-2)*MSDOS_SB(sb)->
 322                             cluster_size+MSDOS_SB(sb)->data_start+count,name,
 323                             number,ino)) >= 0) return cluster;
 324                 }
 325                 if (!(start = fat_access(sb,start,-1))) panic("FAT error");
 326         }
 327         while (start != -1);
 328         return -ENOENT;
 329 }
 330 
 331 
 332 static int raw_scan(struct super_block *sb,int start,char *name,int number,
     /* [previous][next][first][last][top][bottom][index][help] */
 333     int *ino)
 334 {
 335     if (start) return raw_scan_nonroot(sb,start,name,number,ino);
 336     else return raw_scan_root(sb,name,number,ino);
 337 }
 338 
 339 
 340 int msdos_parent_ino(struct inode *dir,int locked)
     /* [previous][next][first][last][top][bottom][index][help] */
 341 {
 342         int error,current,prev,this;
 343 
 344         if (!S_ISDIR(dir->i_mode)) panic("Non-directory fed to m_p_i");
 345         if (dir->i_ino == MSDOS_ROOT_INO) return dir->i_ino;
 346         if (!locked) lock_creation(); /* prevent renames */
 347         if ((current = raw_scan(dir->i_sb,dir->i_data[D_START],MSDOS_DOTDOT,0,
 348             NULL)) < 0) {
 349                 if (!locked) unlock_creation();
 350                 return current;
 351         }
 352         if (!current) this = MSDOS_ROOT_INO;
 353         else {
 354                 if ((prev = raw_scan(dir->i_sb,current,MSDOS_DOTDOT,0,NULL)) <
 355                     0) {
 356                         if (!locked) unlock_creation();
 357                         return prev;
 358                 }
 359                 if ((error = raw_scan(dir->i_sb,prev,NULL,current,&this)) < 0) {
 360                         if (!locked) unlock_creation();
 361                         return error;
 362                 }
 363         }
 364         if (!locked) unlock_creation();
 365         return this;
 366 }

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