The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/fs/fat/cache.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*
    2  *  linux/fs/fat/cache.c
    3  *
    4  *  Written 1992,1993 by Werner Almesberger
    5  *
    6  *  Mar 1999. AV. Changed cache, so that it uses the starting cluster instead
    7  *      of inode number.
    8  *  May 1999. AV. Fixed the bogosity with FAT32 (read "FAT28"). Fscking lusers.
    9  */
   10 
   11 #include <linux/msdos_fs.h>
   12 #include <linux/kernel.h>
   13 #include <linux/errno.h>
   14 #include <linux/string.h>
   15 #include <linux/stat.h>
   16 #include <linux/fat_cvf.h>
   17 
   18 #if 0
   19 #  define PRINTK(x) printk x
   20 #else
   21 #  define PRINTK(x)
   22 #endif
   23 
   24 static struct fat_cache *fat_cache,cache[FAT_CACHE];
   25 static spinlock_t fat_cache_lock = SPIN_LOCK_UNLOCKED;
   26 
   27 /* Returns the this'th FAT entry, -1 if it is an end-of-file entry. If
   28    new_value is != -1, that FAT entry is replaced by it. */
   29 
   30 int fat_access(struct super_block *sb,int nr,int new_value)
   31 {
   32         return MSDOS_SB(sb)->cvf_format->fat_access(sb,nr,new_value);
   33 }
   34 
   35 int fat_bmap(struct inode *inode,int sector)
   36 {
   37         return MSDOS_SB(inode->i_sb)->cvf_format->cvf_bmap(inode,sector);
   38 }
   39 
   40 int default_fat_access(struct super_block *sb,int nr,int new_value)
   41 {
   42         struct buffer_head *bh, *bh2, *c_bh, *c_bh2;
   43         unsigned char *p_first, *p_last;
   44         int copy, first, last, next, b;
   45 
   46         if ((unsigned) (nr-2) >= MSDOS_SB(sb)->clusters)
   47                 return 0;
   48         if (MSDOS_SB(sb)->fat_bits == 32) {
   49                 first = last = nr*4;
   50         } else if (MSDOS_SB(sb)->fat_bits == 16) {
   51                 first = last = nr*2;
   52         } else {
   53                 first = nr*3/2;
   54                 last = first+1;
   55         }
   56         b = MSDOS_SB(sb)->fat_start + (first >> sb->s_blocksize_bits);
   57         if (!(bh = fat_bread(sb, b))) {
   58                 printk("bread in fat_access failed\n");
   59                 return 0;
   60         }
   61         if ((first >> sb->s_blocksize_bits) == (last >> sb->s_blocksize_bits)) {
   62                 bh2 = bh;
   63         } else {
   64                 if (!(bh2 = fat_bread(sb, b+1))) {
   65                         fat_brelse(sb, bh);
   66                         printk("2nd bread in fat_access failed\n");
   67                         return 0;
   68                 }
   69         }
   70         if (MSDOS_SB(sb)->fat_bits == 32) {
   71                 p_first = p_last = NULL; /* GCC needs that stuff */
   72                 next = CF_LE_L(((__u32 *) bh->b_data)[(first &
   73                     (sb->s_blocksize - 1)) >> 2]);
   74                 /* Fscking Microsoft marketing department. Their "32" is 28. */
   75                 next &= 0xfffffff;
   76                 if (next >= 0xffffff7) next = -1;
   77                 PRINTK(("fat_bread: 0x%x, nr=0x%x, first=0x%x, next=0x%x\n", b, nr, first, next));
   78 
   79         } else if (MSDOS_SB(sb)->fat_bits == 16) {
   80                 p_first = p_last = NULL; /* GCC needs that stuff */
   81                 next = CF_LE_W(((__u16 *) bh->b_data)[(first &
   82                     (sb->s_blocksize - 1)) >> 1]);
   83                 if (next >= 0xfff7) next = -1;
   84         } else {
   85                 p_first = &((__u8 *)bh->b_data)[first & (sb->s_blocksize - 1)];
   86                 p_last = &((__u8 *)bh2->b_data)[(first + 1) & (sb->s_blocksize - 1)];
   87                 if (nr & 1) next = ((*p_first >> 4) | (*p_last << 4)) & 0xfff;
   88                 else next = (*p_first+(*p_last << 8)) & 0xfff;
   89                 if (next >= 0xff7) next = -1;
   90         }
   91         if (new_value != -1) {
   92                 if (MSDOS_SB(sb)->fat_bits == 32) {
   93                         ((__u32 *)bh->b_data)[(first & (sb->s_blocksize - 1)) >> 2]
   94                                 = CT_LE_L(new_value);
   95                 } else if (MSDOS_SB(sb)->fat_bits == 16) {
   96                         ((__u16 *)bh->b_data)[(first & (sb->s_blocksize - 1)) >> 1]
   97                                 = CT_LE_W(new_value);
   98                 } else {
   99                         if (nr & 1) {
  100                                 *p_first = (*p_first & 0xf) | (new_value << 4);
  101                                 *p_last = new_value >> 4;
  102                         }
  103                         else {
  104                                 *p_first = new_value & 0xff;
  105                                 *p_last = (*p_last & 0xf0) | (new_value >> 8);
  106                         }
  107                         fat_mark_buffer_dirty(sb, bh2);
  108                 }
  109                 fat_mark_buffer_dirty(sb, bh);
  110                 for (copy = 1; copy < MSDOS_SB(sb)->fats; copy++) {
  111                         b = MSDOS_SB(sb)->fat_start + (first >> sb->s_blocksize_bits)
  112                                 + MSDOS_SB(sb)->fat_length * copy;
  113                         if (!(c_bh = fat_bread(sb, b)))
  114                                 break;
  115                         if (bh != bh2) {
  116                                 if (!(c_bh2 = fat_bread(sb, b+1))) {
  117                                         fat_brelse(sb, c_bh);
  118                                         break;
  119                                 }
  120                                 memcpy(c_bh2->b_data, bh2->b_data, sb->s_blocksize);
  121                                 fat_mark_buffer_dirty(sb, c_bh2);
  122                                 fat_brelse(sb, c_bh2);
  123                         }
  124                         memcpy(c_bh->b_data, bh->b_data, sb->s_blocksize);
  125                         fat_mark_buffer_dirty(sb, c_bh);
  126                         fat_brelse(sb, c_bh);
  127                 }
  128         }
  129         fat_brelse(sb, bh);
  130         if (bh != bh2)
  131                 fat_brelse(sb, bh2);
  132         return next;
  133 }
  134 
  135 void fat_cache_init(void)
  136 {
  137         static int initialized = 0;
  138         int count;
  139 
  140         spin_lock(&fat_cache_lock);
  141         if (initialized) {
  142                 spin_unlock(&fat_cache_lock);
  143                 return;
  144         }
  145         fat_cache = &cache[0];
  146         for (count = 0; count < FAT_CACHE; count++) {
  147                 cache[count].device = 0;
  148                 cache[count].next = count == FAT_CACHE-1 ? NULL :
  149                     &cache[count+1];
  150         }
  151         initialized = 1;
  152         spin_unlock(&fat_cache_lock);
  153 }
  154 
  155 
  156 void fat_cache_lookup(struct inode *inode,int cluster,int *f_clu,int *d_clu)
  157 {
  158         struct fat_cache *walk;
  159         int first = MSDOS_I(inode)->i_start;
  160 
  161         if (!first)
  162                 return;
  163         spin_lock(&fat_cache_lock);
  164         for (walk = fat_cache; walk; walk = walk->next)
  165                 if (inode->i_dev == walk->device
  166                     && walk->start_cluster == first
  167                     && walk->file_cluster <= cluster
  168                     && walk->file_cluster > *f_clu) {
  169                         *d_clu = walk->disk_cluster;
  170 #ifdef DEBUG
  171 printk("cache hit: %d (%d)\n",walk->file_cluster,*d_clu);
  172 #endif
  173                         if ((*f_clu = walk->file_cluster) == cluster) { 
  174                                 spin_unlock(&fat_cache_lock);
  175                                 return;
  176                         }
  177                 }
  178         spin_unlock(&fat_cache_lock);
  179 #ifdef DEBUG
  180 printk("cache miss\n");
  181 #endif
  182 }
  183 
  184 
  185 #ifdef DEBUG
  186 static void list_cache(void)
  187 {
  188         struct fat_cache *walk;
  189 
  190         for (walk = fat_cache; walk; walk = walk->next) {
  191                 if (walk->device)
  192                         printk("<%s,%d>(%d,%d) ", kdevname(walk->device),
  193                                walk->start_cluster, walk->file_cluster,
  194                                walk->disk_cluster);
  195                 else printk("-- ");
  196         }
  197         printk("\n");
  198 }
  199 #endif
  200 
  201 
  202 void fat_cache_add(struct inode *inode,int f_clu,int d_clu)
  203 {
  204         struct fat_cache *walk,*last;
  205         int first = MSDOS_I(inode)->i_start;
  206 
  207         last = NULL;
  208         spin_lock(&fat_cache_lock);
  209         for (walk = fat_cache; walk->next; walk = (last = walk)->next)
  210                 if (inode->i_dev == walk->device
  211                     && walk->start_cluster == first
  212                     && walk->file_cluster == f_clu) {
  213                         if (walk->disk_cluster != d_clu) {
  214                                 printk("FAT cache corruption inode=%ld\n",
  215                                         inode->i_ino);
  216                                 spin_unlock(&fat_cache_lock);
  217                                 fat_cache_inval_inode(inode);
  218                                 return;
  219                         }
  220                         /* update LRU */
  221                         if (last == NULL) {
  222                                 spin_unlock(&fat_cache_lock);
  223                                 return;
  224                         }
  225                         last->next = walk->next;
  226                         walk->next = fat_cache;
  227                         fat_cache = walk;
  228 #ifdef DEBUG
  229 list_cache();
  230 #endif
  231                         spin_unlock(&fat_cache_lock);
  232                         return;
  233                 }
  234         walk->device = inode->i_dev;
  235         walk->start_cluster = first;
  236         walk->file_cluster = f_clu;
  237         walk->disk_cluster = d_clu;
  238         last->next = NULL;
  239         walk->next = fat_cache;
  240         fat_cache = walk;
  241         spin_unlock(&fat_cache_lock);
  242 #ifdef DEBUG
  243 list_cache();
  244 #endif
  245 }
  246 
  247 
  248 /* Cache invalidation occurs rarely, thus the LRU chain is not updated. It
  249    fixes itself after a while. */
  250 
  251 void fat_cache_inval_inode(struct inode *inode)
  252 {
  253         struct fat_cache *walk;
  254         int first = MSDOS_I(inode)->i_start;
  255 
  256         spin_lock(&fat_cache_lock);
  257         for (walk = fat_cache; walk; walk = walk->next)
  258                 if (walk->device == inode->i_dev
  259                     && walk->start_cluster == first)
  260                         walk->device = 0;
  261         spin_unlock(&fat_cache_lock);
  262 }
  263 
  264 
  265 void fat_cache_inval_dev(kdev_t device)
  266 {
  267         struct fat_cache *walk;
  268 
  269         spin_lock(&fat_cache_lock);
  270         for (walk = fat_cache; walk; walk = walk->next)
  271                 if (walk->device == device)
  272                         walk->device = 0;
  273         spin_unlock(&fat_cache_lock);
  274 }
  275 
  276 
  277 int fat_get_cluster(struct inode *inode,int cluster)
  278 {
  279         int nr,count;
  280 
  281         if (!(nr = MSDOS_I(inode)->i_start)) return 0;
  282         if (!cluster) return nr;
  283         count = 0;
  284         for (fat_cache_lookup(inode,cluster,&count,&nr); count < cluster;
  285             count++) {
  286                 if ((nr = fat_access(inode->i_sb,nr,-1)) == -1) return 0;
  287                 if (!nr) return 0;
  288         }
  289         fat_cache_add(inode,cluster,nr);
  290         return nr;
  291 }
  292 
  293 int default_fat_bmap(struct inode *inode,int sector)
  294 {
  295         struct super_block *sb = inode->i_sb;
  296         struct msdos_sb_info *sbi = MSDOS_SB(sb);
  297         int cluster, offset, last_block;
  298 
  299         if ((sbi->fat_bits != 32) &&
  300             (inode->i_ino == MSDOS_ROOT_INO || (S_ISDIR(inode->i_mode) &&
  301              !MSDOS_I(inode)->i_start))) {
  302                 if (sector >= sbi->dir_entries >> sbi->dir_per_block_bits)
  303                         return 0;
  304                 return sector + sbi->dir_start;
  305         }
  306         last_block = (MSDOS_I(inode)->mmu_private + (sb->s_blocksize - 1))
  307                 >> sb->s_blocksize_bits;
  308         if (sector >= last_block)
  309                 return 0;
  310 
  311         cluster = sector / sbi->cluster_size;
  312         offset  = sector % sbi->cluster_size;
  313         if (!(cluster = fat_get_cluster(inode, cluster)))
  314                 return 0;
  315 
  316         return (cluster - 2) * sbi->cluster_size + sbi->data_start + offset;
  317 }
  318 
  319 
  320 /* Free all clusters after the skip'th cluster. Doesn't use the cache,
  321    because this way we get an additional sanity check. */
  322 
  323 int fat_free(struct inode *inode,int skip)
  324 {
  325         int nr,last;
  326 
  327         if (!(nr = MSDOS_I(inode)->i_start)) return 0;
  328         last = 0;
  329         while (skip--) {
  330                 last = nr;
  331                 if ((nr = fat_access(inode->i_sb,nr,-1)) == -1) return 0;
  332                 if (!nr) {
  333                         printk("fat_free: skipped EOF\n");
  334                         return -EIO;
  335                 }
  336         }
  337         if (last) {
  338                 fat_access(inode->i_sb,last,EOF_FAT(inode->i_sb));
  339                 fat_cache_inval_inode(inode);
  340         } else {
  341                 fat_cache_inval_inode(inode);
  342                 MSDOS_I(inode)->i_start = 0;
  343                 MSDOS_I(inode)->i_logstart = 0;
  344                 mark_inode_dirty(inode);
  345         }
  346         lock_fat(inode->i_sb);
  347         while (nr != -1) {
  348                 if (!(nr = fat_access(inode->i_sb,nr,0))) {
  349                         fat_fs_panic(inode->i_sb,"fat_free: deleting beyond EOF");
  350                         break;
  351                 }
  352                 if (MSDOS_SB(inode->i_sb)->free_clusters != -1) {
  353                         MSDOS_SB(inode->i_sb)->free_clusters++;
  354                         if (MSDOS_SB(inode->i_sb)->fat_bits == 32) {
  355                                 fat_clusters_flush(inode->i_sb);
  356                         }
  357                 }
  358                 inode->i_blocks -= (1 << MSDOS_SB(inode->i_sb)->cluster_bits) / 512;
  359         }
  360         unlock_fat(inode->i_sb);
  361         return 0;
  362 }

Cache object: e833966d430ad078d1307880b8e3f942


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.