Creation of Cybook 2416 (actually Gen4) repository
This commit is contained in:
7
fs/fat/Makefile
Normal file
7
fs/fat/Makefile
Normal file
@@ -0,0 +1,7 @@
|
||||
#
|
||||
# Makefile for the Linux fat filesystem support.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_FAT_FS) += fat.o
|
||||
|
||||
fat-objs := cache.o dir.o fatent.o file.o inode.o misc.o
|
||||
331
fs/fat/cache.c
Normal file
331
fs/fat/cache.c
Normal file
@@ -0,0 +1,331 @@
|
||||
/*
|
||||
* linux/fs/fat/cache.c
|
||||
*
|
||||
* Written 1992,1993 by Werner Almesberger
|
||||
*
|
||||
* Mar 1999. AV. Changed cache, so that it uses the starting cluster instead
|
||||
* of inode number.
|
||||
* May 1999. AV. Fixed the bogosity with FAT32 (read "FAT28"). Fscking lusers.
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/msdos_fs.h>
|
||||
#include <linux/buffer_head.h>
|
||||
|
||||
/* this must be > 0. */
|
||||
#define FAT_MAX_CACHE 8
|
||||
|
||||
struct fat_cache {
|
||||
struct list_head cache_list;
|
||||
int nr_contig; /* number of contiguous clusters */
|
||||
int fcluster; /* cluster number in the file. */
|
||||
int dcluster; /* cluster number on disk. */
|
||||
};
|
||||
|
||||
struct fat_cache_id {
|
||||
unsigned int id;
|
||||
int nr_contig;
|
||||
int fcluster;
|
||||
int dcluster;
|
||||
};
|
||||
|
||||
static inline int fat_max_cache(struct inode *inode)
|
||||
{
|
||||
return FAT_MAX_CACHE;
|
||||
}
|
||||
|
||||
static struct kmem_cache *fat_cache_cachep;
|
||||
|
||||
static void init_once(void *foo, struct kmem_cache *cachep, unsigned long flags)
|
||||
{
|
||||
struct fat_cache *cache = (struct fat_cache *)foo;
|
||||
|
||||
if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
|
||||
SLAB_CTOR_CONSTRUCTOR)
|
||||
INIT_LIST_HEAD(&cache->cache_list);
|
||||
}
|
||||
|
||||
int __init fat_cache_init(void)
|
||||
{
|
||||
fat_cache_cachep = kmem_cache_create("fat_cache",
|
||||
sizeof(struct fat_cache),
|
||||
0, SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD,
|
||||
init_once, NULL);
|
||||
if (fat_cache_cachep == NULL)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fat_cache_destroy(void)
|
||||
{
|
||||
kmem_cache_destroy(fat_cache_cachep);
|
||||
}
|
||||
|
||||
static inline struct fat_cache *fat_cache_alloc(struct inode *inode)
|
||||
{
|
||||
return kmem_cache_alloc(fat_cache_cachep, GFP_KERNEL);
|
||||
}
|
||||
|
||||
static inline void fat_cache_free(struct fat_cache *cache)
|
||||
{
|
||||
BUG_ON(!list_empty(&cache->cache_list));
|
||||
kmem_cache_free(fat_cache_cachep, cache);
|
||||
}
|
||||
|
||||
static inline void fat_cache_update_lru(struct inode *inode,
|
||||
struct fat_cache *cache)
|
||||
{
|
||||
if (MSDOS_I(inode)->cache_lru.next != &cache->cache_list)
|
||||
list_move(&cache->cache_list, &MSDOS_I(inode)->cache_lru);
|
||||
}
|
||||
|
||||
static int fat_cache_lookup(struct inode *inode, int fclus,
|
||||
struct fat_cache_id *cid,
|
||||
int *cached_fclus, int *cached_dclus)
|
||||
{
|
||||
static struct fat_cache nohit = { .fcluster = 0, };
|
||||
|
||||
struct fat_cache *hit = &nohit, *p;
|
||||
int offset = -1;
|
||||
|
||||
spin_lock(&MSDOS_I(inode)->cache_lru_lock);
|
||||
list_for_each_entry(p, &MSDOS_I(inode)->cache_lru, cache_list) {
|
||||
/* Find the cache of "fclus" or nearest cache. */
|
||||
if (p->fcluster <= fclus && hit->fcluster < p->fcluster) {
|
||||
hit = p;
|
||||
if ((hit->fcluster + hit->nr_contig) < fclus) {
|
||||
offset = hit->nr_contig;
|
||||
} else {
|
||||
offset = fclus - hit->fcluster;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hit != &nohit) {
|
||||
fat_cache_update_lru(inode, hit);
|
||||
|
||||
cid->id = MSDOS_I(inode)->cache_valid_id;
|
||||
cid->nr_contig = hit->nr_contig;
|
||||
cid->fcluster = hit->fcluster;
|
||||
cid->dcluster = hit->dcluster;
|
||||
*cached_fclus = cid->fcluster + offset;
|
||||
*cached_dclus = cid->dcluster + offset;
|
||||
}
|
||||
spin_unlock(&MSDOS_I(inode)->cache_lru_lock);
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
static struct fat_cache *fat_cache_merge(struct inode *inode,
|
||||
struct fat_cache_id *new)
|
||||
{
|
||||
struct fat_cache *p;
|
||||
|
||||
list_for_each_entry(p, &MSDOS_I(inode)->cache_lru, cache_list) {
|
||||
/* Find the same part as "new" in cluster-chain. */
|
||||
if (p->fcluster == new->fcluster) {
|
||||
BUG_ON(p->dcluster != new->dcluster);
|
||||
if (new->nr_contig > p->nr_contig)
|
||||
p->nr_contig = new->nr_contig;
|
||||
return p;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void fat_cache_add(struct inode *inode, struct fat_cache_id *new)
|
||||
{
|
||||
struct fat_cache *cache, *tmp;
|
||||
|
||||
if (new->fcluster == -1) /* dummy cache */
|
||||
return;
|
||||
|
||||
spin_lock(&MSDOS_I(inode)->cache_lru_lock);
|
||||
if (new->id != FAT_CACHE_VALID &&
|
||||
new->id != MSDOS_I(inode)->cache_valid_id)
|
||||
goto out; /* this cache was invalidated */
|
||||
|
||||
cache = fat_cache_merge(inode, new);
|
||||
if (cache == NULL) {
|
||||
if (MSDOS_I(inode)->nr_caches < fat_max_cache(inode)) {
|
||||
MSDOS_I(inode)->nr_caches++;
|
||||
spin_unlock(&MSDOS_I(inode)->cache_lru_lock);
|
||||
|
||||
tmp = fat_cache_alloc(inode);
|
||||
spin_lock(&MSDOS_I(inode)->cache_lru_lock);
|
||||
cache = fat_cache_merge(inode, new);
|
||||
if (cache != NULL) {
|
||||
MSDOS_I(inode)->nr_caches--;
|
||||
fat_cache_free(tmp);
|
||||
goto out_update_lru;
|
||||
}
|
||||
cache = tmp;
|
||||
} else {
|
||||
struct list_head *p = MSDOS_I(inode)->cache_lru.prev;
|
||||
cache = list_entry(p, struct fat_cache, cache_list);
|
||||
}
|
||||
cache->fcluster = new->fcluster;
|
||||
cache->dcluster = new->dcluster;
|
||||
cache->nr_contig = new->nr_contig;
|
||||
}
|
||||
out_update_lru:
|
||||
fat_cache_update_lru(inode, cache);
|
||||
out:
|
||||
spin_unlock(&MSDOS_I(inode)->cache_lru_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Cache invalidation occurs rarely, thus the LRU chain is not updated. It
|
||||
* fixes itself after a while.
|
||||
*/
|
||||
static void __fat_cache_inval_inode(struct inode *inode)
|
||||
{
|
||||
struct msdos_inode_info *i = MSDOS_I(inode);
|
||||
struct fat_cache *cache;
|
||||
|
||||
while (!list_empty(&i->cache_lru)) {
|
||||
cache = list_entry(i->cache_lru.next, struct fat_cache, cache_list);
|
||||
list_del_init(&cache->cache_list);
|
||||
i->nr_caches--;
|
||||
fat_cache_free(cache);
|
||||
}
|
||||
/* Update. The copy of caches before this id is discarded. */
|
||||
i->cache_valid_id++;
|
||||
if (i->cache_valid_id == FAT_CACHE_VALID)
|
||||
i->cache_valid_id++;
|
||||
}
|
||||
|
||||
void fat_cache_inval_inode(struct inode *inode)
|
||||
{
|
||||
spin_lock(&MSDOS_I(inode)->cache_lru_lock);
|
||||
__fat_cache_inval_inode(inode);
|
||||
spin_unlock(&MSDOS_I(inode)->cache_lru_lock);
|
||||
}
|
||||
|
||||
static inline int cache_contiguous(struct fat_cache_id *cid, int dclus)
|
||||
{
|
||||
cid->nr_contig++;
|
||||
return ((cid->dcluster + cid->nr_contig) == dclus);
|
||||
}
|
||||
|
||||
static inline void cache_init(struct fat_cache_id *cid, int fclus, int dclus)
|
||||
{
|
||||
cid->id = FAT_CACHE_VALID;
|
||||
cid->fcluster = fclus;
|
||||
cid->dcluster = dclus;
|
||||
cid->nr_contig = 0;
|
||||
}
|
||||
|
||||
int fat_get_cluster(struct inode *inode, int cluster, int *fclus, int *dclus)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
const int limit = sb->s_maxbytes >> MSDOS_SB(sb)->cluster_bits;
|
||||
struct fat_entry fatent;
|
||||
struct fat_cache_id cid;
|
||||
int nr;
|
||||
|
||||
BUG_ON(MSDOS_I(inode)->i_start == 0);
|
||||
|
||||
*fclus = 0;
|
||||
*dclus = MSDOS_I(inode)->i_start;
|
||||
if (cluster == 0)
|
||||
return 0;
|
||||
|
||||
if (fat_cache_lookup(inode, cluster, &cid, fclus, dclus) < 0) {
|
||||
/*
|
||||
* dummy, always not contiguous
|
||||
* This is reinitialized by cache_init(), later.
|
||||
*/
|
||||
cache_init(&cid, -1, -1);
|
||||
}
|
||||
|
||||
fatent_init(&fatent);
|
||||
while (*fclus < cluster) {
|
||||
/* prevent the infinite loop of cluster chain */
|
||||
if (*fclus > limit) {
|
||||
fat_fs_panic(sb, "%s: detected the cluster chain loop"
|
||||
" (i_pos %lld)", __FUNCTION__,
|
||||
MSDOS_I(inode)->i_pos);
|
||||
nr = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
nr = fat_ent_read(inode, &fatent, *dclus);
|
||||
if (nr < 0)
|
||||
goto out;
|
||||
else if (nr == FAT_ENT_FREE) {
|
||||
fat_fs_panic(sb, "%s: invalid cluster chain"
|
||||
" (i_pos %lld)", __FUNCTION__,
|
||||
MSDOS_I(inode)->i_pos);
|
||||
nr = -EIO;
|
||||
goto out;
|
||||
} else if (nr == FAT_ENT_EOF) {
|
||||
fat_cache_add(inode, &cid);
|
||||
goto out;
|
||||
}
|
||||
(*fclus)++;
|
||||
*dclus = nr;
|
||||
if (!cache_contiguous(&cid, *dclus))
|
||||
cache_init(&cid, *fclus, *dclus);
|
||||
}
|
||||
nr = 0;
|
||||
fat_cache_add(inode, &cid);
|
||||
out:
|
||||
fatent_brelse(&fatent);
|
||||
return nr;
|
||||
}
|
||||
|
||||
static int fat_bmap_cluster(struct inode *inode, int cluster)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
int ret, fclus, dclus;
|
||||
|
||||
if (MSDOS_I(inode)->i_start == 0)
|
||||
return 0;
|
||||
|
||||
ret = fat_get_cluster(inode, cluster, &fclus, &dclus);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else if (ret == FAT_ENT_EOF) {
|
||||
fat_fs_panic(sb, "%s: request beyond EOF (i_pos %lld)",
|
||||
__FUNCTION__, MSDOS_I(inode)->i_pos);
|
||||
return -EIO;
|
||||
}
|
||||
return dclus;
|
||||
}
|
||||
|
||||
int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys,
|
||||
unsigned long *mapped_blocks)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(sb);
|
||||
sector_t last_block;
|
||||
int cluster, offset;
|
||||
|
||||
*phys = 0;
|
||||
*mapped_blocks = 0;
|
||||
if ((sbi->fat_bits != 32) && (inode->i_ino == MSDOS_ROOT_INO)) {
|
||||
if (sector < (sbi->dir_entries >> sbi->dir_per_block_bits)) {
|
||||
*phys = sector + sbi->dir_start;
|
||||
*mapped_blocks = 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
last_block = (MSDOS_I(inode)->mmu_private + (sb->s_blocksize - 1))
|
||||
>> sb->s_blocksize_bits;
|
||||
if (sector >= last_block)
|
||||
return 0;
|
||||
|
||||
cluster = sector >> (sbi->cluster_bits - sb->s_blocksize_bits);
|
||||
offset = sector & (sbi->sec_per_clus - 1);
|
||||
cluster = fat_bmap_cluster(inode, cluster);
|
||||
if (cluster < 0)
|
||||
return cluster;
|
||||
else if (cluster) {
|
||||
*phys = fat_clus_to_blknr(sbi, cluster) + offset;
|
||||
*mapped_blocks = sbi->sec_per_clus - offset;
|
||||
if (*mapped_blocks > last_block - sector)
|
||||
*mapped_blocks = last_block - sector;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
1324
fs/fat/dir.c
Normal file
1324
fs/fat/dir.c
Normal file
File diff suppressed because it is too large
Load Diff
616
fs/fat/fatent.c
Normal file
616
fs/fat/fatent.c
Normal file
@@ -0,0 +1,616 @@
|
||||
/*
|
||||
* Copyright (C) 2004, OGAWA Hirofumi
|
||||
* Released under GPL v2.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/msdos_fs.h>
|
||||
|
||||
struct fatent_operations {
|
||||
void (*ent_blocknr)(struct super_block *, int, int *, sector_t *);
|
||||
void (*ent_set_ptr)(struct fat_entry *, int);
|
||||
int (*ent_bread)(struct super_block *, struct fat_entry *,
|
||||
int, sector_t);
|
||||
int (*ent_get)(struct fat_entry *);
|
||||
void (*ent_put)(struct fat_entry *, int);
|
||||
int (*ent_next)(struct fat_entry *);
|
||||
};
|
||||
|
||||
static void fat12_ent_blocknr(struct super_block *sb, int entry,
|
||||
int *offset, sector_t *blocknr)
|
||||
{
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(sb);
|
||||
int bytes = entry + (entry >> 1);
|
||||
WARN_ON(entry < FAT_START_ENT || sbi->max_cluster <= entry);
|
||||
*offset = bytes & (sb->s_blocksize - 1);
|
||||
*blocknr = sbi->fat_start + (bytes >> sb->s_blocksize_bits);
|
||||
}
|
||||
|
||||
static void fat_ent_blocknr(struct super_block *sb, int entry,
|
||||
int *offset, sector_t *blocknr)
|
||||
{
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(sb);
|
||||
int bytes = (entry << sbi->fatent_shift);
|
||||
WARN_ON(entry < FAT_START_ENT || sbi->max_cluster <= entry);
|
||||
*offset = bytes & (sb->s_blocksize - 1);
|
||||
*blocknr = sbi->fat_start + (bytes >> sb->s_blocksize_bits);
|
||||
}
|
||||
|
||||
static void fat12_ent_set_ptr(struct fat_entry *fatent, int offset)
|
||||
{
|
||||
struct buffer_head **bhs = fatent->bhs;
|
||||
if (fatent->nr_bhs == 1) {
|
||||
WARN_ON(offset >= (bhs[0]->b_size - 1));
|
||||
fatent->u.ent12_p[0] = bhs[0]->b_data + offset;
|
||||
fatent->u.ent12_p[1] = bhs[0]->b_data + (offset + 1);
|
||||
} else {
|
||||
WARN_ON(offset != (bhs[0]->b_size - 1));
|
||||
fatent->u.ent12_p[0] = bhs[0]->b_data + offset;
|
||||
fatent->u.ent12_p[1] = bhs[1]->b_data;
|
||||
}
|
||||
}
|
||||
|
||||
static void fat16_ent_set_ptr(struct fat_entry *fatent, int offset)
|
||||
{
|
||||
WARN_ON(offset & (2 - 1));
|
||||
fatent->u.ent16_p = (__le16 *)(fatent->bhs[0]->b_data + offset);
|
||||
}
|
||||
|
||||
static void fat32_ent_set_ptr(struct fat_entry *fatent, int offset)
|
||||
{
|
||||
WARN_ON(offset & (4 - 1));
|
||||
fatent->u.ent32_p = (__le32 *)(fatent->bhs[0]->b_data + offset);
|
||||
}
|
||||
|
||||
static int fat12_ent_bread(struct super_block *sb, struct fat_entry *fatent,
|
||||
int offset, sector_t blocknr)
|
||||
{
|
||||
struct buffer_head **bhs = fatent->bhs;
|
||||
|
||||
WARN_ON(blocknr < MSDOS_SB(sb)->fat_start);
|
||||
bhs[0] = sb_bread(sb, blocknr);
|
||||
if (!bhs[0])
|
||||
goto err;
|
||||
|
||||
if ((offset + 1) < sb->s_blocksize)
|
||||
fatent->nr_bhs = 1;
|
||||
else {
|
||||
/* This entry is block boundary, it needs the next block */
|
||||
blocknr++;
|
||||
bhs[1] = sb_bread(sb, blocknr);
|
||||
if (!bhs[1])
|
||||
goto err_brelse;
|
||||
fatent->nr_bhs = 2;
|
||||
}
|
||||
fat12_ent_set_ptr(fatent, offset);
|
||||
return 0;
|
||||
|
||||
err_brelse:
|
||||
brelse(bhs[0]);
|
||||
err:
|
||||
printk(KERN_ERR "FAT: FAT read failed (blocknr %llu)\n",
|
||||
(unsigned long long)blocknr);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int fat_ent_bread(struct super_block *sb, struct fat_entry *fatent,
|
||||
int offset, sector_t blocknr)
|
||||
{
|
||||
struct fatent_operations *ops = MSDOS_SB(sb)->fatent_ops;
|
||||
|
||||
WARN_ON(blocknr < MSDOS_SB(sb)->fat_start);
|
||||
fatent->bhs[0] = sb_bread(sb, blocknr);
|
||||
if (!fatent->bhs[0]) {
|
||||
printk(KERN_ERR "FAT: FAT read failed (blocknr %llu)\n",
|
||||
(unsigned long long)blocknr);
|
||||
return -EIO;
|
||||
}
|
||||
fatent->nr_bhs = 1;
|
||||
ops->ent_set_ptr(fatent, offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fat12_ent_get(struct fat_entry *fatent)
|
||||
{
|
||||
u8 **ent12_p = fatent->u.ent12_p;
|
||||
int next;
|
||||
|
||||
if (fatent->entry & 1)
|
||||
next = (*ent12_p[0] >> 4) | (*ent12_p[1] << 4);
|
||||
else
|
||||
next = (*ent12_p[1] << 8) | *ent12_p[0];
|
||||
next &= 0x0fff;
|
||||
if (next >= BAD_FAT12)
|
||||
next = FAT_ENT_EOF;
|
||||
return next;
|
||||
}
|
||||
|
||||
static int fat16_ent_get(struct fat_entry *fatent)
|
||||
{
|
||||
int next = le16_to_cpu(*fatent->u.ent16_p);
|
||||
WARN_ON((unsigned long)fatent->u.ent16_p & (2 - 1));
|
||||
if (next >= BAD_FAT16)
|
||||
next = FAT_ENT_EOF;
|
||||
return next;
|
||||
}
|
||||
|
||||
static int fat32_ent_get(struct fat_entry *fatent)
|
||||
{
|
||||
int next = le32_to_cpu(*fatent->u.ent32_p) & 0x0fffffff;
|
||||
WARN_ON((unsigned long)fatent->u.ent32_p & (4 - 1));
|
||||
if (next >= BAD_FAT32)
|
||||
next = FAT_ENT_EOF;
|
||||
return next;
|
||||
}
|
||||
|
||||
static void fat12_ent_put(struct fat_entry *fatent, int new)
|
||||
{
|
||||
u8 **ent12_p = fatent->u.ent12_p;
|
||||
|
||||
if (new == FAT_ENT_EOF)
|
||||
new = EOF_FAT12;
|
||||
|
||||
if (fatent->entry & 1) {
|
||||
*ent12_p[0] = (new << 4) | (*ent12_p[0] & 0x0f);
|
||||
*ent12_p[1] = new >> 4;
|
||||
} else {
|
||||
*ent12_p[0] = new & 0xff;
|
||||
*ent12_p[1] = (*ent12_p[1] & 0xf0) | (new >> 8);
|
||||
}
|
||||
|
||||
mark_buffer_dirty(fatent->bhs[0]);
|
||||
if (fatent->nr_bhs == 2)
|
||||
mark_buffer_dirty(fatent->bhs[1]);
|
||||
}
|
||||
|
||||
static void fat16_ent_put(struct fat_entry *fatent, int new)
|
||||
{
|
||||
if (new == FAT_ENT_EOF)
|
||||
new = EOF_FAT16;
|
||||
|
||||
*fatent->u.ent16_p = cpu_to_le16(new);
|
||||
mark_buffer_dirty(fatent->bhs[0]);
|
||||
}
|
||||
|
||||
static void fat32_ent_put(struct fat_entry *fatent, int new)
|
||||
{
|
||||
if (new == FAT_ENT_EOF)
|
||||
new = EOF_FAT32;
|
||||
|
||||
WARN_ON(new & 0xf0000000);
|
||||
new |= le32_to_cpu(*fatent->u.ent32_p) & ~0x0fffffff;
|
||||
*fatent->u.ent32_p = cpu_to_le32(new);
|
||||
mark_buffer_dirty(fatent->bhs[0]);
|
||||
}
|
||||
|
||||
static int fat12_ent_next(struct fat_entry *fatent)
|
||||
{
|
||||
u8 **ent12_p = fatent->u.ent12_p;
|
||||
struct buffer_head **bhs = fatent->bhs;
|
||||
u8 *nextp = ent12_p[1] + 1 + (fatent->entry & 1);
|
||||
|
||||
fatent->entry++;
|
||||
if (fatent->nr_bhs == 1) {
|
||||
WARN_ON(ent12_p[0] > (u8 *)(bhs[0]->b_data + (bhs[0]->b_size - 2)));
|
||||
WARN_ON(ent12_p[1] > (u8 *)(bhs[0]->b_data + (bhs[0]->b_size - 1)));
|
||||
if (nextp < (u8 *)(bhs[0]->b_data + (bhs[0]->b_size - 1))) {
|
||||
ent12_p[0] = nextp - 1;
|
||||
ent12_p[1] = nextp;
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
WARN_ON(ent12_p[0] != (u8 *)(bhs[0]->b_data + (bhs[0]->b_size - 1)));
|
||||
WARN_ON(ent12_p[1] != (u8 *)bhs[1]->b_data);
|
||||
ent12_p[0] = nextp - 1;
|
||||
ent12_p[1] = nextp;
|
||||
brelse(bhs[0]);
|
||||
bhs[0] = bhs[1];
|
||||
fatent->nr_bhs = 1;
|
||||
return 1;
|
||||
}
|
||||
ent12_p[0] = NULL;
|
||||
ent12_p[1] = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fat16_ent_next(struct fat_entry *fatent)
|
||||
{
|
||||
const struct buffer_head *bh = fatent->bhs[0];
|
||||
fatent->entry++;
|
||||
if (fatent->u.ent16_p < (__le16 *)(bh->b_data + (bh->b_size - 2))) {
|
||||
fatent->u.ent16_p++;
|
||||
return 1;
|
||||
}
|
||||
fatent->u.ent16_p = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fat32_ent_next(struct fat_entry *fatent)
|
||||
{
|
||||
const struct buffer_head *bh = fatent->bhs[0];
|
||||
fatent->entry++;
|
||||
if (fatent->u.ent32_p < (__le32 *)(bh->b_data + (bh->b_size - 4))) {
|
||||
fatent->u.ent32_p++;
|
||||
return 1;
|
||||
}
|
||||
fatent->u.ent32_p = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct fatent_operations fat12_ops = {
|
||||
.ent_blocknr = fat12_ent_blocknr,
|
||||
.ent_set_ptr = fat12_ent_set_ptr,
|
||||
.ent_bread = fat12_ent_bread,
|
||||
.ent_get = fat12_ent_get,
|
||||
.ent_put = fat12_ent_put,
|
||||
.ent_next = fat12_ent_next,
|
||||
};
|
||||
|
||||
static struct fatent_operations fat16_ops = {
|
||||
.ent_blocknr = fat_ent_blocknr,
|
||||
.ent_set_ptr = fat16_ent_set_ptr,
|
||||
.ent_bread = fat_ent_bread,
|
||||
.ent_get = fat16_ent_get,
|
||||
.ent_put = fat16_ent_put,
|
||||
.ent_next = fat16_ent_next,
|
||||
};
|
||||
|
||||
static struct fatent_operations fat32_ops = {
|
||||
.ent_blocknr = fat_ent_blocknr,
|
||||
.ent_set_ptr = fat32_ent_set_ptr,
|
||||
.ent_bread = fat_ent_bread,
|
||||
.ent_get = fat32_ent_get,
|
||||
.ent_put = fat32_ent_put,
|
||||
.ent_next = fat32_ent_next,
|
||||
};
|
||||
|
||||
static inline void lock_fat(struct msdos_sb_info *sbi)
|
||||
{
|
||||
mutex_lock(&sbi->fat_lock);
|
||||
}
|
||||
|
||||
static inline void unlock_fat(struct msdos_sb_info *sbi)
|
||||
{
|
||||
mutex_unlock(&sbi->fat_lock);
|
||||
}
|
||||
|
||||
void fat_ent_access_init(struct super_block *sb)
|
||||
{
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(sb);
|
||||
|
||||
mutex_init(&sbi->fat_lock);
|
||||
|
||||
switch (sbi->fat_bits) {
|
||||
case 32:
|
||||
sbi->fatent_shift = 2;
|
||||
sbi->fatent_ops = &fat32_ops;
|
||||
break;
|
||||
case 16:
|
||||
sbi->fatent_shift = 1;
|
||||
sbi->fatent_ops = &fat16_ops;
|
||||
break;
|
||||
case 12:
|
||||
sbi->fatent_shift = -1;
|
||||
sbi->fatent_ops = &fat12_ops;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int fat_ent_update_ptr(struct super_block *sb,
|
||||
struct fat_entry *fatent,
|
||||
int offset, sector_t blocknr)
|
||||
{
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(sb);
|
||||
struct fatent_operations *ops = sbi->fatent_ops;
|
||||
struct buffer_head **bhs = fatent->bhs;
|
||||
|
||||
/* Is this fatent's blocks including this entry? */
|
||||
if (!fatent->nr_bhs || bhs[0]->b_blocknr != blocknr)
|
||||
return 0;
|
||||
/* Does this entry need the next block? */
|
||||
if (sbi->fat_bits == 12 && (offset + 1) >= sb->s_blocksize) {
|
||||
if (fatent->nr_bhs != 2 || bhs[1]->b_blocknr != (blocknr + 1))
|
||||
return 0;
|
||||
}
|
||||
ops->ent_set_ptr(fatent, offset);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int fat_ent_read(struct inode *inode, struct fat_entry *fatent, int entry)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
|
||||
struct fatent_operations *ops = sbi->fatent_ops;
|
||||
int err, offset;
|
||||
sector_t blocknr;
|
||||
|
||||
if (entry < FAT_START_ENT || sbi->max_cluster <= entry) {
|
||||
fatent_brelse(fatent);
|
||||
fat_fs_panic(sb, "invalid access to FAT (entry 0x%08x)", entry);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
fatent_set_entry(fatent, entry);
|
||||
ops->ent_blocknr(sb, entry, &offset, &blocknr);
|
||||
|
||||
if (!fat_ent_update_ptr(sb, fatent, offset, blocknr)) {
|
||||
fatent_brelse(fatent);
|
||||
err = ops->ent_bread(sb, fatent, offset, blocknr);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
return ops->ent_get(fatent);
|
||||
}
|
||||
|
||||
/* FIXME: We can write the blocks as more big chunk. */
|
||||
static int fat_mirror_bhs(struct super_block *sb, struct buffer_head **bhs,
|
||||
int nr_bhs)
|
||||
{
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(sb);
|
||||
struct buffer_head *c_bh;
|
||||
int err, n, copy;
|
||||
|
||||
err = 0;
|
||||
for (copy = 1; copy < sbi->fats; copy++) {
|
||||
sector_t backup_fat = sbi->fat_length * copy;
|
||||
|
||||
for (n = 0; n < nr_bhs; n++) {
|
||||
c_bh = sb_getblk(sb, backup_fat + bhs[n]->b_blocknr);
|
||||
if (!c_bh) {
|
||||
err = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
memcpy(c_bh->b_data, bhs[n]->b_data, sb->s_blocksize);
|
||||
set_buffer_uptodate(c_bh);
|
||||
mark_buffer_dirty(c_bh);
|
||||
if (sb->s_flags & MS_SYNCHRONOUS)
|
||||
err = sync_dirty_buffer(c_bh);
|
||||
brelse(c_bh);
|
||||
if (err)
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
error:
|
||||
return err;
|
||||
}
|
||||
|
||||
int fat_ent_write(struct inode *inode, struct fat_entry *fatent,
|
||||
int new, int wait)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct fatent_operations *ops = MSDOS_SB(sb)->fatent_ops;
|
||||
int err;
|
||||
|
||||
ops->ent_put(fatent, new);
|
||||
if (wait) {
|
||||
err = fat_sync_bhs(fatent->bhs, fatent->nr_bhs);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
return fat_mirror_bhs(sb, fatent->bhs, fatent->nr_bhs);
|
||||
}
|
||||
|
||||
static inline int fat_ent_next(struct msdos_sb_info *sbi,
|
||||
struct fat_entry *fatent)
|
||||
{
|
||||
if (sbi->fatent_ops->ent_next(fatent)) {
|
||||
if (fatent->entry < sbi->max_cluster)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int fat_ent_read_block(struct super_block *sb,
|
||||
struct fat_entry *fatent)
|
||||
{
|
||||
struct fatent_operations *ops = MSDOS_SB(sb)->fatent_ops;
|
||||
sector_t blocknr;
|
||||
int offset;
|
||||
|
||||
fatent_brelse(fatent);
|
||||
ops->ent_blocknr(sb, fatent->entry, &offset, &blocknr);
|
||||
return ops->ent_bread(sb, fatent, offset, blocknr);
|
||||
}
|
||||
|
||||
static void fat_collect_bhs(struct buffer_head **bhs, int *nr_bhs,
|
||||
struct fat_entry *fatent)
|
||||
{
|
||||
int n, i;
|
||||
|
||||
for (n = 0; n < fatent->nr_bhs; n++) {
|
||||
for (i = 0; i < *nr_bhs; i++) {
|
||||
if (fatent->bhs[n] == bhs[i])
|
||||
break;
|
||||
}
|
||||
if (i == *nr_bhs) {
|
||||
get_bh(fatent->bhs[n]);
|
||||
bhs[i] = fatent->bhs[n];
|
||||
(*nr_bhs)++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int fat_alloc_clusters(struct inode *inode, int *cluster, int nr_cluster)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(sb);
|
||||
struct fatent_operations *ops = sbi->fatent_ops;
|
||||
struct fat_entry fatent, prev_ent;
|
||||
struct buffer_head *bhs[MAX_BUF_PER_PAGE];
|
||||
int i, count, err, nr_bhs, idx_clus;
|
||||
|
||||
BUG_ON(nr_cluster > (MAX_BUF_PER_PAGE / 2)); /* fixed limit */
|
||||
|
||||
lock_fat(sbi);
|
||||
if (sbi->free_clusters != -1 && sbi->free_clusters < nr_cluster) {
|
||||
unlock_fat(sbi);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
err = nr_bhs = idx_clus = 0;
|
||||
count = FAT_START_ENT;
|
||||
fatent_init(&prev_ent);
|
||||
fatent_init(&fatent);
|
||||
fatent_set_entry(&fatent, sbi->prev_free + 1);
|
||||
while (count < sbi->max_cluster) {
|
||||
if (fatent.entry >= sbi->max_cluster)
|
||||
fatent.entry = FAT_START_ENT;
|
||||
fatent_set_entry(&fatent, fatent.entry);
|
||||
err = fat_ent_read_block(sb, &fatent);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
/* Find the free entries in a block */
|
||||
do {
|
||||
if (ops->ent_get(&fatent) == FAT_ENT_FREE) {
|
||||
int entry = fatent.entry;
|
||||
|
||||
/* make the cluster chain */
|
||||
ops->ent_put(&fatent, FAT_ENT_EOF);
|
||||
if (prev_ent.nr_bhs)
|
||||
ops->ent_put(&prev_ent, entry);
|
||||
|
||||
fat_collect_bhs(bhs, &nr_bhs, &fatent);
|
||||
|
||||
sbi->prev_free = entry;
|
||||
if (sbi->free_clusters != -1)
|
||||
sbi->free_clusters--;
|
||||
sb->s_dirt = 1;
|
||||
|
||||
cluster[idx_clus] = entry;
|
||||
idx_clus++;
|
||||
if (idx_clus == nr_cluster)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* fat_collect_bhs() gets ref-count of bhs,
|
||||
* so we can still use the prev_ent.
|
||||
*/
|
||||
prev_ent = fatent;
|
||||
}
|
||||
count++;
|
||||
if (count == sbi->max_cluster)
|
||||
break;
|
||||
} while (fat_ent_next(sbi, &fatent));
|
||||
}
|
||||
|
||||
/* Couldn't allocate the free entries */
|
||||
sbi->free_clusters = 0;
|
||||
sb->s_dirt = 1;
|
||||
err = -ENOSPC;
|
||||
|
||||
out:
|
||||
unlock_fat(sbi);
|
||||
fatent_brelse(&fatent);
|
||||
if (!err) {
|
||||
if (inode_needs_sync(inode))
|
||||
err = fat_sync_bhs(bhs, nr_bhs);
|
||||
if (!err)
|
||||
err = fat_mirror_bhs(sb, bhs, nr_bhs);
|
||||
}
|
||||
for (i = 0; i < nr_bhs; i++)
|
||||
brelse(bhs[i]);
|
||||
|
||||
if (err && idx_clus)
|
||||
fat_free_clusters(inode, cluster[0]);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int fat_free_clusters(struct inode *inode, int cluster)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(sb);
|
||||
struct fatent_operations *ops = sbi->fatent_ops;
|
||||
struct fat_entry fatent;
|
||||
struct buffer_head *bhs[MAX_BUF_PER_PAGE];
|
||||
int i, err, nr_bhs;
|
||||
|
||||
nr_bhs = 0;
|
||||
fatent_init(&fatent);
|
||||
lock_fat(sbi);
|
||||
do {
|
||||
cluster = fat_ent_read(inode, &fatent, cluster);
|
||||
if (cluster < 0) {
|
||||
err = cluster;
|
||||
goto error;
|
||||
} else if (cluster == FAT_ENT_FREE) {
|
||||
fat_fs_panic(sb, "%s: deleting FAT entry beyond EOF",
|
||||
__FUNCTION__);
|
||||
err = -EIO;
|
||||
goto error;
|
||||
}
|
||||
|
||||
ops->ent_put(&fatent, FAT_ENT_FREE);
|
||||
if (sbi->free_clusters != -1) {
|
||||
sbi->free_clusters++;
|
||||
sb->s_dirt = 1;
|
||||
}
|
||||
|
||||
if (nr_bhs + fatent.nr_bhs > MAX_BUF_PER_PAGE) {
|
||||
if (sb->s_flags & MS_SYNCHRONOUS) {
|
||||
err = fat_sync_bhs(bhs, nr_bhs);
|
||||
if (err)
|
||||
goto error;
|
||||
}
|
||||
err = fat_mirror_bhs(sb, bhs, nr_bhs);
|
||||
if (err)
|
||||
goto error;
|
||||
for (i = 0; i < nr_bhs; i++)
|
||||
brelse(bhs[i]);
|
||||
nr_bhs = 0;
|
||||
}
|
||||
fat_collect_bhs(bhs, &nr_bhs, &fatent);
|
||||
} while (cluster != FAT_ENT_EOF);
|
||||
|
||||
if (sb->s_flags & MS_SYNCHRONOUS) {
|
||||
err = fat_sync_bhs(bhs, nr_bhs);
|
||||
if (err)
|
||||
goto error;
|
||||
}
|
||||
err = fat_mirror_bhs(sb, bhs, nr_bhs);
|
||||
error:
|
||||
fatent_brelse(&fatent);
|
||||
for (i = 0; i < nr_bhs; i++)
|
||||
brelse(bhs[i]);
|
||||
unlock_fat(sbi);
|
||||
|
||||
fat_clusters_flush(sb);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(fat_free_clusters);
|
||||
|
||||
int fat_count_free_clusters(struct super_block *sb)
|
||||
{
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(sb);
|
||||
struct fatent_operations *ops = sbi->fatent_ops;
|
||||
struct fat_entry fatent;
|
||||
int err = 0, free;
|
||||
|
||||
lock_fat(sbi);
|
||||
if (sbi->free_clusters != -1)
|
||||
goto out;
|
||||
|
||||
free = 0;
|
||||
fatent_init(&fatent);
|
||||
fatent_set_entry(&fatent, FAT_START_ENT);
|
||||
while (fatent.entry < sbi->max_cluster) {
|
||||
err = fat_ent_read_block(sb, &fatent);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
do {
|
||||
if (ops->ent_get(&fatent) == FAT_ENT_FREE)
|
||||
free++;
|
||||
} while (fat_ent_next(sbi, &fatent));
|
||||
}
|
||||
sbi->free_clusters = free;
|
||||
sb->s_dirt = 1;
|
||||
fatent_brelse(&fatent);
|
||||
out:
|
||||
unlock_fat(sbi);
|
||||
return err;
|
||||
}
|
||||
319
fs/fat/file.c
Normal file
319
fs/fat/file.c
Normal file
@@ -0,0 +1,319 @@
|
||||
/*
|
||||
* linux/fs/fat/file.c
|
||||
*
|
||||
* Written 1992,1993 by Werner Almesberger
|
||||
*
|
||||
* regular file handling primitives for fat-based filesystems
|
||||
*/
|
||||
|
||||
#include <linux/capability.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/msdos_fs.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/writeback.h>
|
||||
#include <linux/backing-dev.h>
|
||||
#include <linux/blkdev.h>
|
||||
|
||||
int fat_generic_ioctl(struct inode *inode, struct file *filp,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
|
||||
u32 __user *user_attr = (u32 __user *)arg;
|
||||
|
||||
switch (cmd) {
|
||||
case FAT_IOCTL_GET_ATTRIBUTES:
|
||||
{
|
||||
u32 attr;
|
||||
|
||||
if (inode->i_ino == MSDOS_ROOT_INO)
|
||||
attr = ATTR_DIR;
|
||||
else
|
||||
attr = fat_attr(inode);
|
||||
|
||||
return put_user(attr, user_attr);
|
||||
}
|
||||
case FAT_IOCTL_SET_ATTRIBUTES:
|
||||
{
|
||||
u32 attr, oldattr;
|
||||
int err, is_dir = S_ISDIR(inode->i_mode);
|
||||
struct iattr ia;
|
||||
|
||||
err = get_user(attr, user_attr);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
|
||||
if (IS_RDONLY(inode)) {
|
||||
err = -EROFS;
|
||||
goto up;
|
||||
}
|
||||
|
||||
/*
|
||||
* ATTR_VOLUME and ATTR_DIR cannot be changed; this also
|
||||
* prevents the user from turning us into a VFAT
|
||||
* longname entry. Also, we obviously can't set
|
||||
* any of the NTFS attributes in the high 24 bits.
|
||||
*/
|
||||
attr &= 0xff & ~(ATTR_VOLUME | ATTR_DIR);
|
||||
/* Merge in ATTR_VOLUME and ATTR_DIR */
|
||||
attr |= (MSDOS_I(inode)->i_attrs & ATTR_VOLUME) |
|
||||
(is_dir ? ATTR_DIR : 0);
|
||||
oldattr = fat_attr(inode);
|
||||
|
||||
/* Equivalent to a chmod() */
|
||||
ia.ia_valid = ATTR_MODE | ATTR_CTIME;
|
||||
if (is_dir) {
|
||||
ia.ia_mode = MSDOS_MKMODE(attr,
|
||||
S_IRWXUGO & ~sbi->options.fs_dmask)
|
||||
| S_IFDIR;
|
||||
} else {
|
||||
ia.ia_mode = MSDOS_MKMODE(attr,
|
||||
(S_IRUGO | S_IWUGO | (inode->i_mode & S_IXUGO))
|
||||
& ~sbi->options.fs_fmask)
|
||||
| S_IFREG;
|
||||
}
|
||||
|
||||
/* The root directory has no attributes */
|
||||
if (inode->i_ino == MSDOS_ROOT_INO && attr != ATTR_DIR) {
|
||||
err = -EINVAL;
|
||||
goto up;
|
||||
}
|
||||
|
||||
if (sbi->options.sys_immutable) {
|
||||
if ((attr | oldattr) & ATTR_SYS) {
|
||||
if (!capable(CAP_LINUX_IMMUTABLE)) {
|
||||
err = -EPERM;
|
||||
goto up;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* This MUST be done before doing anything irreversible... */
|
||||
err = notify_change(filp->f_path.dentry, &ia);
|
||||
if (err)
|
||||
goto up;
|
||||
|
||||
if (sbi->options.sys_immutable) {
|
||||
if (attr & ATTR_SYS)
|
||||
inode->i_flags |= S_IMMUTABLE;
|
||||
else
|
||||
inode->i_flags &= S_IMMUTABLE;
|
||||
}
|
||||
|
||||
MSDOS_I(inode)->i_attrs = attr & ATTR_UNUSED;
|
||||
mark_inode_dirty(inode);
|
||||
up:
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
return err;
|
||||
}
|
||||
default:
|
||||
return -ENOTTY; /* Inappropriate ioctl for device */
|
||||
}
|
||||
}
|
||||
|
||||
static int fat_file_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
if ((filp->f_mode & FMODE_WRITE) &&
|
||||
MSDOS_SB(inode->i_sb)->options.flush) {
|
||||
fat_flush_inodes(inode->i_sb, inode, NULL);
|
||||
congestion_wait(WRITE, HZ/10);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct file_operations fat_file_operations = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read = do_sync_read,
|
||||
.write = do_sync_write,
|
||||
.aio_read = generic_file_aio_read,
|
||||
.aio_write = generic_file_aio_write,
|
||||
.mmap = generic_file_mmap,
|
||||
.release = fat_file_release,
|
||||
.ioctl = fat_generic_ioctl,
|
||||
.fsync = file_fsync,
|
||||
.sendfile = generic_file_sendfile,
|
||||
};
|
||||
|
||||
static int fat_cont_expand(struct inode *inode, loff_t size)
|
||||
{
|
||||
struct address_space *mapping = inode->i_mapping;
|
||||
loff_t start = inode->i_size, count = size - inode->i_size;
|
||||
int err;
|
||||
|
||||
err = generic_cont_expand_simple(inode, size);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC;
|
||||
mark_inode_dirty(inode);
|
||||
if (IS_SYNC(inode))
|
||||
err = sync_page_range_nolock(inode, mapping, start, count);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
int fat_notify_change(struct dentry *dentry, struct iattr *attr)
|
||||
{
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb);
|
||||
struct inode *inode = dentry->d_inode;
|
||||
int mask, error = 0;
|
||||
|
||||
lock_kernel();
|
||||
|
||||
/*
|
||||
* Expand the file. Since inode_setattr() updates ->i_size
|
||||
* before calling the ->truncate(), but FAT needs to fill the
|
||||
* hole before it.
|
||||
*/
|
||||
if (attr->ia_valid & ATTR_SIZE) {
|
||||
if (attr->ia_size > inode->i_size) {
|
||||
error = fat_cont_expand(inode, attr->ia_size);
|
||||
if (error || attr->ia_valid == ATTR_SIZE)
|
||||
goto out;
|
||||
attr->ia_valid &= ~ATTR_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
error = inode_change_ok(inode, attr);
|
||||
if (error) {
|
||||
if (sbi->options.quiet)
|
||||
error = 0;
|
||||
goto out;
|
||||
}
|
||||
if (((attr->ia_valid & ATTR_UID) &&
|
||||
(attr->ia_uid != sbi->options.fs_uid)) ||
|
||||
((attr->ia_valid & ATTR_GID) &&
|
||||
(attr->ia_gid != sbi->options.fs_gid)) ||
|
||||
((attr->ia_valid & ATTR_MODE) &&
|
||||
(attr->ia_mode & ~MSDOS_VALID_MODE)))
|
||||
error = -EPERM;
|
||||
|
||||
if (error) {
|
||||
if (sbi->options.quiet)
|
||||
error = 0;
|
||||
goto out;
|
||||
}
|
||||
error = inode_setattr(inode, attr);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
mask = sbi->options.fs_dmask;
|
||||
else
|
||||
mask = sbi->options.fs_fmask;
|
||||
inode->i_mode &= S_IFMT | (S_IRWXUGO & ~mask);
|
||||
out:
|
||||
unlock_kernel();
|
||||
return error;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(fat_notify_change);
|
||||
|
||||
/* Free all clusters after the skip'th cluster. */
|
||||
static int fat_free(struct inode *inode, int skip)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
int err, wait, free_start, i_start, i_logstart;
|
||||
|
||||
if (MSDOS_I(inode)->i_start == 0)
|
||||
return 0;
|
||||
|
||||
fat_cache_inval_inode(inode);
|
||||
|
||||
wait = IS_DIRSYNC(inode);
|
||||
i_start = free_start = MSDOS_I(inode)->i_start;
|
||||
i_logstart = MSDOS_I(inode)->i_logstart;
|
||||
|
||||
/* First, we write the new file size. */
|
||||
if (!skip) {
|
||||
MSDOS_I(inode)->i_start = 0;
|
||||
MSDOS_I(inode)->i_logstart = 0;
|
||||
}
|
||||
MSDOS_I(inode)->i_attrs |= ATTR_ARCH;
|
||||
inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC;
|
||||
if (wait) {
|
||||
err = fat_sync_inode(inode);
|
||||
if (err) {
|
||||
MSDOS_I(inode)->i_start = i_start;
|
||||
MSDOS_I(inode)->i_logstart = i_logstart;
|
||||
return err;
|
||||
}
|
||||
} else
|
||||
mark_inode_dirty(inode);
|
||||
|
||||
/* Write a new EOF, and get the remaining cluster chain for freeing. */
|
||||
if (skip) {
|
||||
struct fat_entry fatent;
|
||||
int ret, fclus, dclus;
|
||||
|
||||
ret = fat_get_cluster(inode, skip - 1, &fclus, &dclus);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else if (ret == FAT_ENT_EOF)
|
||||
return 0;
|
||||
|
||||
fatent_init(&fatent);
|
||||
ret = fat_ent_read(inode, &fatent, dclus);
|
||||
if (ret == FAT_ENT_EOF) {
|
||||
fatent_brelse(&fatent);
|
||||
return 0;
|
||||
} else if (ret == FAT_ENT_FREE) {
|
||||
fat_fs_panic(sb,
|
||||
"%s: invalid cluster chain (i_pos %lld)",
|
||||
__FUNCTION__, MSDOS_I(inode)->i_pos);
|
||||
ret = -EIO;
|
||||
} else if (ret > 0) {
|
||||
err = fat_ent_write(inode, &fatent, FAT_ENT_EOF, wait);
|
||||
if (err)
|
||||
ret = err;
|
||||
}
|
||||
fatent_brelse(&fatent);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
free_start = ret;
|
||||
}
|
||||
inode->i_blocks = skip << (MSDOS_SB(sb)->cluster_bits - 9);
|
||||
|
||||
/* Freeing the remained cluster chain */
|
||||
return fat_free_clusters(inode, free_start);
|
||||
}
|
||||
|
||||
void fat_truncate(struct inode *inode)
|
||||
{
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
|
||||
const unsigned int cluster_size = sbi->cluster_size;
|
||||
int nr_clusters;
|
||||
|
||||
/*
|
||||
* This protects against truncating a file bigger than it was then
|
||||
* trying to write into the hole.
|
||||
*/
|
||||
if (MSDOS_I(inode)->mmu_private > inode->i_size)
|
||||
MSDOS_I(inode)->mmu_private = inode->i_size;
|
||||
|
||||
nr_clusters = (inode->i_size + (cluster_size - 1)) >> sbi->cluster_bits;
|
||||
|
||||
lock_kernel();
|
||||
fat_free(inode, nr_clusters);
|
||||
unlock_kernel();
|
||||
fat_flush_inodes(inode->i_sb, inode, NULL);
|
||||
}
|
||||
|
||||
int fat_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
generic_fillattr(inode, stat);
|
||||
stat->blksize = MSDOS_SB(inode->i_sb)->cluster_size;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fat_getattr);
|
||||
|
||||
const struct inode_operations fat_file_inode_operations = {
|
||||
.truncate = fat_truncate,
|
||||
.setattr = fat_notify_change,
|
||||
.getattr = fat_getattr,
|
||||
};
|
||||
1513
fs/fat/inode.c
Normal file
1513
fs/fat/inode.c
Normal file
File diff suppressed because it is too large
Load Diff
212
fs/fat/misc.c
Normal file
212
fs/fat/misc.c
Normal file
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* linux/fs/fat/misc.c
|
||||
*
|
||||
* Written 1992,1993 by Werner Almesberger
|
||||
* 22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980
|
||||
* and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru)
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/msdos_fs.h>
|
||||
#include <linux/buffer_head.h>
|
||||
|
||||
/*
|
||||
* fat_fs_panic reports a severe file system problem and sets the file system
|
||||
* read-only. The file system can be made writable again by remounting it.
|
||||
*/
|
||||
void fat_fs_panic(struct super_block *s, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
printk(KERN_ERR "FAT: Filesystem panic (dev %s)\n", s->s_id);
|
||||
|
||||
printk(KERN_ERR " ");
|
||||
va_start(args, fmt);
|
||||
vprintk(fmt, args);
|
||||
va_end(args);
|
||||
printk("\n");
|
||||
|
||||
if (!(s->s_flags & MS_RDONLY)) {
|
||||
s->s_flags |= MS_RDONLY;
|
||||
printk(KERN_ERR " File system has been set read-only\n");
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(fat_fs_panic);
|
||||
|
||||
/* Flushes the number of free clusters on FAT32 */
|
||||
/* XXX: Need to write one per FSINFO block. Currently only writes 1 */
|
||||
void fat_clusters_flush(struct super_block *sb)
|
||||
{
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(sb);
|
||||
struct buffer_head *bh;
|
||||
struct fat_boot_fsinfo *fsinfo;
|
||||
|
||||
if (sbi->fat_bits != 32)
|
||||
return;
|
||||
|
||||
bh = sb_bread(sb, sbi->fsinfo_sector);
|
||||
if (bh == NULL) {
|
||||
printk(KERN_ERR "FAT: bread failed in fat_clusters_flush\n");
|
||||
return;
|
||||
}
|
||||
|
||||
fsinfo = (struct fat_boot_fsinfo *)bh->b_data;
|
||||
/* Sanity check */
|
||||
if (!IS_FSINFO(fsinfo)) {
|
||||
printk(KERN_ERR "FAT: Did not find valid FSINFO signature.\n"
|
||||
" Found signature1 0x%08x signature2 0x%08x"
|
||||
" (sector = %lu)\n",
|
||||
le32_to_cpu(fsinfo->signature1),
|
||||
le32_to_cpu(fsinfo->signature2),
|
||||
sbi->fsinfo_sector);
|
||||
} else {
|
||||
if (sbi->free_clusters != -1)
|
||||
fsinfo->free_clusters = cpu_to_le32(sbi->free_clusters);
|
||||
if (sbi->prev_free != -1)
|
||||
fsinfo->next_cluster = cpu_to_le32(sbi->prev_free);
|
||||
mark_buffer_dirty(bh);
|
||||
}
|
||||
brelse(bh);
|
||||
}
|
||||
|
||||
/*
|
||||
* fat_chain_add() adds a new cluster to the chain of clusters represented
|
||||
* by inode.
|
||||
*/
|
||||
int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(sb);
|
||||
int ret, new_fclus, last;
|
||||
|
||||
/*
|
||||
* We must locate the last cluster of the file to add this new
|
||||
* one (new_dclus) to the end of the link list (the FAT).
|
||||
*/
|
||||
last = new_fclus = 0;
|
||||
if (MSDOS_I(inode)->i_start) {
|
||||
int fclus, dclus;
|
||||
|
||||
ret = fat_get_cluster(inode, FAT_ENT_EOF, &fclus, &dclus);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
new_fclus = fclus + 1;
|
||||
last = dclus;
|
||||
}
|
||||
|
||||
/* add new one to the last of the cluster chain */
|
||||
if (last) {
|
||||
struct fat_entry fatent;
|
||||
|
||||
fatent_init(&fatent);
|
||||
ret = fat_ent_read(inode, &fatent, last);
|
||||
if (ret >= 0) {
|
||||
int wait = inode_needs_sync(inode);
|
||||
ret = fat_ent_write(inode, &fatent, new_dclus, wait);
|
||||
fatent_brelse(&fatent);
|
||||
}
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
// fat_cache_add(inode, new_fclus, new_dclus);
|
||||
} else {
|
||||
MSDOS_I(inode)->i_start = new_dclus;
|
||||
MSDOS_I(inode)->i_logstart = new_dclus;
|
||||
/*
|
||||
* Since generic_osync_inode() synchronize later if
|
||||
* this is not directory, we don't here.
|
||||
*/
|
||||
if (S_ISDIR(inode->i_mode) && IS_DIRSYNC(inode)) {
|
||||
ret = fat_sync_inode(inode);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else
|
||||
mark_inode_dirty(inode);
|
||||
}
|
||||
if (new_fclus != (inode->i_blocks >> (sbi->cluster_bits - 9))) {
|
||||
fat_fs_panic(sb, "clusters badly computed (%d != %lu)",
|
||||
new_fclus, inode->i_blocks >> (sbi->cluster_bits - 9));
|
||||
fat_cache_inval_inode(inode);
|
||||
}
|
||||
inode->i_blocks += nr_cluster << (sbi->cluster_bits - 9);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern struct timezone sys_tz;
|
||||
|
||||
/* Linear day numbers of the respective 1sts in non-leap years. */
|
||||
static int day_n[] = {
|
||||
/* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */
|
||||
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
|
||||
int date_dos2unix(unsigned short time, unsigned short date)
|
||||
{
|
||||
int month, year, secs;
|
||||
|
||||
/*
|
||||
* first subtract and mask after that... Otherwise, if
|
||||
* date == 0, bad things happen
|
||||
*/
|
||||
month = ((date >> 5) - 1) & 15;
|
||||
year = date >> 9;
|
||||
secs = (time & 31)*2+60*((time >> 5) & 63)+(time >> 11)*3600+86400*
|
||||
((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 &&
|
||||
month < 2 ? 1 : 0)+3653);
|
||||
/* days since 1.1.70 plus 80's leap day */
|
||||
secs += sys_tz.tz_minuteswest*60;
|
||||
return secs;
|
||||
}
|
||||
|
||||
/* Convert linear UNIX date to a MS-DOS time/date pair. */
|
||||
void fat_date_unix2dos(int unix_date, __le16 *time, __le16 *date)
|
||||
{
|
||||
int day, year, nl_day, month;
|
||||
|
||||
unix_date -= sys_tz.tz_minuteswest*60;
|
||||
|
||||
/* Jan 1 GMT 00:00:00 1980. But what about another time zone? */
|
||||
if (unix_date < 315532800)
|
||||
unix_date = 315532800;
|
||||
|
||||
*time = cpu_to_le16((unix_date % 60)/2+(((unix_date/60) % 60) << 5)+
|
||||
(((unix_date/3600) % 24) << 11));
|
||||
day = unix_date/86400-3652;
|
||||
year = day/365;
|
||||
if ((year+3)/4+365*year > day)
|
||||
year--;
|
||||
day -= (year+3)/4+365*year;
|
||||
if (day == 59 && !(year & 3)) {
|
||||
nl_day = day;
|
||||
month = 2;
|
||||
} else {
|
||||
nl_day = (year & 3) || day <= 59 ? day : day-1;
|
||||
for (month = 0; month < 12; month++) {
|
||||
if (day_n[month] > nl_day)
|
||||
break;
|
||||
}
|
||||
}
|
||||
*date = cpu_to_le16(nl_day-day_n[month-1]+1+(month << 5)+(year << 9));
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(fat_date_unix2dos);
|
||||
|
||||
int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs)
|
||||
{
|
||||
int i, err = 0;
|
||||
|
||||
ll_rw_block(SWRITE, nr_bhs, bhs);
|
||||
for (i = 0; i < nr_bhs; i++) {
|
||||
wait_on_buffer(bhs[i]);
|
||||
if (buffer_eopnotsupp(bhs[i])) {
|
||||
clear_buffer_eopnotsupp(bhs[i]);
|
||||
err = -EOPNOTSUPP;
|
||||
} else if (!err && !buffer_uptodate(bhs[i]))
|
||||
err = -EIO;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user