mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-29 11:11:45 -04:00
Merge branch 'master' into next
Conflicts: security/keys/internal.h security/keys/process_keys.c security/keys/request_key.c Fixed conflicts above by using the non 'tsk' versions. Signed-off-by: James Morris <[email protected]>
This commit is contained in:
commit
2b82892565
538 changed files with 20101 additions and 5812 deletions
|
|
@ -3,5 +3,9 @@
|
|||
#
|
||||
|
||||
obj-$(CONFIG_FAT_FS) += fat.o
|
||||
obj-$(CONFIG_VFAT_FS) += vfat.o
|
||||
obj-$(CONFIG_MSDOS_FS) += msdos.o
|
||||
|
||||
fat-objs := cache.o dir.o fatent.o file.o inode.o misc.o
|
||||
fat-y := cache.o dir.o fatent.o file.o inode.o misc.o
|
||||
vfat-y := namei_vfat.o
|
||||
msdos-y := namei_msdos.o
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@
|
|||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/msdos_fs.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include "fat.h"
|
||||
|
||||
/* this must be > 0. */
|
||||
#define FAT_MAX_CACHE 8
|
||||
|
|
@ -293,10 +293,12 @@ static int fat_bmap_cluster(struct inode *inode, int cluster)
|
|||
}
|
||||
|
||||
int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys,
|
||||
unsigned long *mapped_blocks)
|
||||
unsigned long *mapped_blocks, int create)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(sb);
|
||||
const unsigned long blocksize = sb->s_blocksize;
|
||||
const unsigned char blocksize_bits = sb->s_blocksize_bits;
|
||||
sector_t last_block;
|
||||
int cluster, offset;
|
||||
|
||||
|
|
@ -309,10 +311,21 @@ int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys,
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
last_block = (MSDOS_I(inode)->mmu_private + (sb->s_blocksize - 1))
|
||||
>> sb->s_blocksize_bits;
|
||||
if (sector >= last_block)
|
||||
return 0;
|
||||
|
||||
last_block = (i_size_read(inode) + (blocksize - 1)) >> blocksize_bits;
|
||||
if (sector >= last_block) {
|
||||
if (!create)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* ->mmu_private can access on only allocation path.
|
||||
* (caller must hold ->i_mutex)
|
||||
*/
|
||||
last_block = (MSDOS_I(inode)->mmu_private + (blocksize - 1))
|
||||
>> blocksize_bits;
|
||||
if (sector >= last_block)
|
||||
return 0;
|
||||
}
|
||||
|
||||
cluster = sector >> (sbi->cluster_bits - sb->s_blocksize_bits);
|
||||
offset = sector & (sbi->sec_per_clus - 1);
|
||||
|
|
|
|||
20
fs/fat/dir.c
20
fs/fat/dir.c
|
|
@ -16,11 +16,11 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/msdos_fs.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/compat.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include "fat.h"
|
||||
|
||||
static inline loff_t fat_make_i_pos(struct super_block *sb,
|
||||
struct buffer_head *bh,
|
||||
|
|
@ -77,7 +77,7 @@ next:
|
|||
|
||||
*bh = NULL;
|
||||
iblock = *pos >> sb->s_blocksize_bits;
|
||||
err = fat_bmap(dir, iblock, &phys, &mapped_blocks);
|
||||
err = fat_bmap(dir, iblock, &phys, &mapped_blocks, 0);
|
||||
if (err || !phys)
|
||||
return -1; /* beyond EOF or error */
|
||||
|
||||
|
|
@ -86,7 +86,7 @@ next:
|
|||
*bh = sb_bread(sb, phys);
|
||||
if (*bh == NULL) {
|
||||
printk(KERN_ERR "FAT: Directory bread(block %llu) failed\n",
|
||||
(unsigned long long)phys);
|
||||
(llu)phys);
|
||||
/* skip this block */
|
||||
*pos = (iblock + 1) << sb->s_blocksize_bits;
|
||||
goto next;
|
||||
|
|
@ -373,9 +373,10 @@ parse_record:
|
|||
if (de->attr == ATTR_EXT) {
|
||||
int status = fat_parse_long(inode, &cpos, &bh, &de,
|
||||
&unicode, &nr_slots);
|
||||
if (status < 0)
|
||||
return status;
|
||||
else if (status == PARSE_INVALID)
|
||||
if (status < 0) {
|
||||
err = status;
|
||||
goto end_of_dir;
|
||||
} else if (status == PARSE_INVALID)
|
||||
continue;
|
||||
else if (status == PARSE_NOT_LONGNAME)
|
||||
goto parse_record;
|
||||
|
|
@ -832,6 +833,7 @@ static long fat_compat_dir_ioctl(struct file *filp, unsigned cmd,
|
|||
#endif /* CONFIG_COMPAT */
|
||||
|
||||
const struct file_operations fat_dir_operations = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read = generic_read_dir,
|
||||
.readdir = fat_readdir,
|
||||
.ioctl = fat_dir_ioctl,
|
||||
|
|
@ -1089,6 +1091,7 @@ int fat_alloc_new_dir(struct inode *dir, struct timespec *ts)
|
|||
struct msdos_dir_entry *de;
|
||||
sector_t blknr;
|
||||
__le16 date, time;
|
||||
u8 time_cs;
|
||||
int err, cluster;
|
||||
|
||||
err = fat_alloc_clusters(dir, &cluster, 1);
|
||||
|
|
@ -1102,7 +1105,7 @@ int fat_alloc_new_dir(struct inode *dir, struct timespec *ts)
|
|||
goto error_free;
|
||||
}
|
||||
|
||||
fat_date_unix2dos(ts->tv_sec, &time, &date, sbi->options.tz_utc);
|
||||
fat_time_unix2fat(sbi, ts, &time, &date, &time_cs);
|
||||
|
||||
de = (struct msdos_dir_entry *)bhs[0]->b_data;
|
||||
/* filling the new directory slots ("." and ".." entries) */
|
||||
|
|
@ -1112,13 +1115,14 @@ int fat_alloc_new_dir(struct inode *dir, struct timespec *ts)
|
|||
de[0].lcase = de[1].lcase = 0;
|
||||
de[0].time = de[1].time = time;
|
||||
de[0].date = de[1].date = date;
|
||||
de[0].ctime_cs = de[1].ctime_cs = 0;
|
||||
if (sbi->options.isvfat) {
|
||||
/* extra timestamps */
|
||||
de[0].ctime = de[1].ctime = time;
|
||||
de[0].ctime_cs = de[1].ctime_cs = time_cs;
|
||||
de[0].adate = de[0].cdate = de[1].adate = de[1].cdate = date;
|
||||
} else {
|
||||
de[0].ctime = de[1].ctime = 0;
|
||||
de[0].ctime_cs = de[1].ctime_cs = 0;
|
||||
de[0].adate = de[0].cdate = de[1].adate = de[1].cdate = 0;
|
||||
}
|
||||
de[0].start = cpu_to_le16(cluster);
|
||||
|
|
|
|||
329
fs/fat/fat.h
Normal file
329
fs/fat/fat.h
Normal file
|
|
@ -0,0 +1,329 @@
|
|||
#ifndef _FAT_H
|
||||
#define _FAT_H
|
||||
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/nls.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/msdos_fs.h>
|
||||
|
||||
/*
|
||||
* vfat shortname flags
|
||||
*/
|
||||
#define VFAT_SFN_DISPLAY_LOWER 0x0001 /* convert to lowercase for display */
|
||||
#define VFAT_SFN_DISPLAY_WIN95 0x0002 /* emulate win95 rule for display */
|
||||
#define VFAT_SFN_DISPLAY_WINNT 0x0004 /* emulate winnt rule for display */
|
||||
#define VFAT_SFN_CREATE_WIN95 0x0100 /* emulate win95 rule for create */
|
||||
#define VFAT_SFN_CREATE_WINNT 0x0200 /* emulate winnt rule for create */
|
||||
|
||||
struct fat_mount_options {
|
||||
uid_t fs_uid;
|
||||
gid_t fs_gid;
|
||||
unsigned short fs_fmask;
|
||||
unsigned short fs_dmask;
|
||||
unsigned short codepage; /* Codepage for shortname conversions */
|
||||
char *iocharset; /* Charset used for filename input/display */
|
||||
unsigned short shortname; /* flags for shortname display/create rule */
|
||||
unsigned char name_check; /* r = relaxed, n = normal, s = strict */
|
||||
unsigned short allow_utime;/* permission for setting the [am]time */
|
||||
unsigned quiet:1, /* set = fake successful chmods and chowns */
|
||||
showexec:1, /* set = only set x bit for com/exe/bat */
|
||||
sys_immutable:1, /* set = system files are immutable */
|
||||
dotsOK:1, /* set = hidden and system files are named '.filename' */
|
||||
isvfat:1, /* 0=no vfat long filename support, 1=vfat support */
|
||||
utf8:1, /* Use of UTF-8 character set (Default) */
|
||||
unicode_xlate:1, /* create escape sequences for unhandled Unicode */
|
||||
numtail:1, /* Does first alias have a numeric '~1' type tail? */
|
||||
flush:1, /* write things quickly */
|
||||
nocase:1, /* Does this need case conversion? 0=need case conversion*/
|
||||
usefree:1, /* Use free_clusters for FAT32 */
|
||||
tz_utc:1, /* Filesystem timestamps are in UTC */
|
||||
rodir:1; /* allow ATTR_RO for directory */
|
||||
};
|
||||
|
||||
#define FAT_HASH_BITS 8
|
||||
#define FAT_HASH_SIZE (1UL << FAT_HASH_BITS)
|
||||
|
||||
/*
|
||||
* MS-DOS file system in-core superblock data
|
||||
*/
|
||||
struct msdos_sb_info {
|
||||
unsigned short sec_per_clus; /* sectors/cluster */
|
||||
unsigned short cluster_bits; /* log2(cluster_size) */
|
||||
unsigned int cluster_size; /* cluster size */
|
||||
unsigned char fats,fat_bits; /* number of FATs, FAT bits (12 or 16) */
|
||||
unsigned short fat_start;
|
||||
unsigned long fat_length; /* FAT start & length (sec.) */
|
||||
unsigned long dir_start;
|
||||
unsigned short dir_entries; /* root dir start & entries */
|
||||
unsigned long data_start; /* first data sector */
|
||||
unsigned long max_cluster; /* maximum cluster number */
|
||||
unsigned long root_cluster; /* first cluster of the root directory */
|
||||
unsigned long fsinfo_sector; /* sector number of FAT32 fsinfo */
|
||||
struct mutex fat_lock;
|
||||
unsigned int prev_free; /* previously allocated cluster number */
|
||||
unsigned int free_clusters; /* -1 if undefined */
|
||||
unsigned int free_clus_valid; /* is free_clusters valid? */
|
||||
struct fat_mount_options options;
|
||||
struct nls_table *nls_disk; /* Codepage used on disk */
|
||||
struct nls_table *nls_io; /* Charset used for input and display */
|
||||
const void *dir_ops; /* Opaque; default directory operations */
|
||||
int dir_per_block; /* dir entries per block */
|
||||
int dir_per_block_bits; /* log2(dir_per_block) */
|
||||
|
||||
int fatent_shift;
|
||||
struct fatent_operations *fatent_ops;
|
||||
|
||||
spinlock_t inode_hash_lock;
|
||||
struct hlist_head inode_hashtable[FAT_HASH_SIZE];
|
||||
};
|
||||
|
||||
#define FAT_CACHE_VALID 0 /* special case for valid cache */
|
||||
|
||||
/*
|
||||
* MS-DOS file system inode data in memory
|
||||
*/
|
||||
struct msdos_inode_info {
|
||||
spinlock_t cache_lru_lock;
|
||||
struct list_head cache_lru;
|
||||
int nr_caches;
|
||||
/* for avoiding the race between fat_free() and fat_get_cluster() */
|
||||
unsigned int cache_valid_id;
|
||||
|
||||
/* NOTE: mmu_private is 64bits, so must hold ->i_mutex to access */
|
||||
loff_t mmu_private; /* physically allocated size */
|
||||
|
||||
int i_start; /* first cluster or 0 */
|
||||
int i_logstart; /* logical first cluster */
|
||||
int i_attrs; /* unused attribute bits */
|
||||
loff_t i_pos; /* on-disk position of directory entry or 0 */
|
||||
struct hlist_node i_fat_hash; /* hash by i_location */
|
||||
struct inode vfs_inode;
|
||||
};
|
||||
|
||||
struct fat_slot_info {
|
||||
loff_t i_pos; /* on-disk position of directory entry */
|
||||
loff_t slot_off; /* offset for slot or de start */
|
||||
int nr_slots; /* number of slots + 1(de) in filename */
|
||||
struct msdos_dir_entry *de;
|
||||
struct buffer_head *bh;
|
||||
};
|
||||
|
||||
static inline struct msdos_sb_info *MSDOS_SB(struct super_block *sb)
|
||||
{
|
||||
return sb->s_fs_info;
|
||||
}
|
||||
|
||||
static inline struct msdos_inode_info *MSDOS_I(struct inode *inode)
|
||||
{
|
||||
return container_of(inode, struct msdos_inode_info, vfs_inode);
|
||||
}
|
||||
|
||||
/*
|
||||
* If ->i_mode can't hold S_IWUGO (i.e. ATTR_RO), we use ->i_attrs to
|
||||
* save ATTR_RO instead of ->i_mode.
|
||||
*
|
||||
* If it's directory and !sbi->options.rodir, ATTR_RO isn't read-only
|
||||
* bit, it's just used as flag for app.
|
||||
*/
|
||||
static inline int fat_mode_can_hold_ro(struct inode *inode)
|
||||
{
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
|
||||
mode_t mask;
|
||||
|
||||
if (S_ISDIR(inode->i_mode)) {
|
||||
if (!sbi->options.rodir)
|
||||
return 0;
|
||||
mask = ~sbi->options.fs_dmask;
|
||||
} else
|
||||
mask = ~sbi->options.fs_fmask;
|
||||
|
||||
if (!(mask & S_IWUGO))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Convert attribute bits and a mask to the UNIX mode. */
|
||||
static inline mode_t fat_make_mode(struct msdos_sb_info *sbi,
|
||||
u8 attrs, mode_t mode)
|
||||
{
|
||||
if (attrs & ATTR_RO && !((attrs & ATTR_DIR) && !sbi->options.rodir))
|
||||
mode &= ~S_IWUGO;
|
||||
|
||||
if (attrs & ATTR_DIR)
|
||||
return (mode & ~sbi->options.fs_dmask) | S_IFDIR;
|
||||
else
|
||||
return (mode & ~sbi->options.fs_fmask) | S_IFREG;
|
||||
}
|
||||
|
||||
/* Return the FAT attribute byte for this inode */
|
||||
static inline u8 fat_make_attrs(struct inode *inode)
|
||||
{
|
||||
u8 attrs = MSDOS_I(inode)->i_attrs;
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
attrs |= ATTR_DIR;
|
||||
if (fat_mode_can_hold_ro(inode) && !(inode->i_mode & S_IWUGO))
|
||||
attrs |= ATTR_RO;
|
||||
return attrs;
|
||||
}
|
||||
|
||||
static inline void fat_save_attrs(struct inode *inode, u8 attrs)
|
||||
{
|
||||
if (fat_mode_can_hold_ro(inode))
|
||||
MSDOS_I(inode)->i_attrs = attrs & ATTR_UNUSED;
|
||||
else
|
||||
MSDOS_I(inode)->i_attrs = attrs & (ATTR_UNUSED | ATTR_RO);
|
||||
}
|
||||
|
||||
static inline unsigned char fat_checksum(const __u8 *name)
|
||||
{
|
||||
unsigned char s = name[0];
|
||||
s = (s<<7) + (s>>1) + name[1]; s = (s<<7) + (s>>1) + name[2];
|
||||
s = (s<<7) + (s>>1) + name[3]; s = (s<<7) + (s>>1) + name[4];
|
||||
s = (s<<7) + (s>>1) + name[5]; s = (s<<7) + (s>>1) + name[6];
|
||||
s = (s<<7) + (s>>1) + name[7]; s = (s<<7) + (s>>1) + name[8];
|
||||
s = (s<<7) + (s>>1) + name[9]; s = (s<<7) + (s>>1) + name[10];
|
||||
return s;
|
||||
}
|
||||
|
||||
static inline sector_t fat_clus_to_blknr(struct msdos_sb_info *sbi, int clus)
|
||||
{
|
||||
return ((sector_t)clus - FAT_START_ENT) * sbi->sec_per_clus
|
||||
+ sbi->data_start;
|
||||
}
|
||||
|
||||
static inline void fat16_towchar(wchar_t *dst, const __u8 *src, size_t len)
|
||||
{
|
||||
#ifdef __BIG_ENDIAN
|
||||
while (len--) {
|
||||
*dst++ = src[0] | (src[1] << 8);
|
||||
src += 2;
|
||||
}
|
||||
#else
|
||||
memcpy(dst, src, len * 2);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void fatwchar_to16(__u8 *dst, const wchar_t *src, size_t len)
|
||||
{
|
||||
#ifdef __BIG_ENDIAN
|
||||
while (len--) {
|
||||
dst[0] = *src & 0x00FF;
|
||||
dst[1] = (*src & 0xFF00) >> 8;
|
||||
dst += 2;
|
||||
src++;
|
||||
}
|
||||
#else
|
||||
memcpy(dst, src, len * 2);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* fat/cache.c */
|
||||
extern void fat_cache_inval_inode(struct inode *inode);
|
||||
extern int fat_get_cluster(struct inode *inode, int cluster,
|
||||
int *fclus, int *dclus);
|
||||
extern int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys,
|
||||
unsigned long *mapped_blocks, int create);
|
||||
|
||||
/* fat/dir.c */
|
||||
extern const struct file_operations fat_dir_operations;
|
||||
extern int fat_search_long(struct inode *inode, const unsigned char *name,
|
||||
int name_len, struct fat_slot_info *sinfo);
|
||||
extern int fat_dir_empty(struct inode *dir);
|
||||
extern int fat_subdirs(struct inode *dir);
|
||||
extern int fat_scan(struct inode *dir, const unsigned char *name,
|
||||
struct fat_slot_info *sinfo);
|
||||
extern int fat_get_dotdot_entry(struct inode *dir, struct buffer_head **bh,
|
||||
struct msdos_dir_entry **de, loff_t *i_pos);
|
||||
extern int fat_alloc_new_dir(struct inode *dir, struct timespec *ts);
|
||||
extern int fat_add_entries(struct inode *dir, void *slots, int nr_slots,
|
||||
struct fat_slot_info *sinfo);
|
||||
extern int fat_remove_entries(struct inode *dir, struct fat_slot_info *sinfo);
|
||||
|
||||
/* fat/fatent.c */
|
||||
struct fat_entry {
|
||||
int entry;
|
||||
union {
|
||||
u8 *ent12_p[2];
|
||||
__le16 *ent16_p;
|
||||
__le32 *ent32_p;
|
||||
} u;
|
||||
int nr_bhs;
|
||||
struct buffer_head *bhs[2];
|
||||
};
|
||||
|
||||
static inline void fatent_init(struct fat_entry *fatent)
|
||||
{
|
||||
fatent->nr_bhs = 0;
|
||||
fatent->entry = 0;
|
||||
fatent->u.ent32_p = NULL;
|
||||
fatent->bhs[0] = fatent->bhs[1] = NULL;
|
||||
}
|
||||
|
||||
static inline void fatent_set_entry(struct fat_entry *fatent, int entry)
|
||||
{
|
||||
fatent->entry = entry;
|
||||
fatent->u.ent32_p = NULL;
|
||||
}
|
||||
|
||||
static inline void fatent_brelse(struct fat_entry *fatent)
|
||||
{
|
||||
int i;
|
||||
fatent->u.ent32_p = NULL;
|
||||
for (i = 0; i < fatent->nr_bhs; i++)
|
||||
brelse(fatent->bhs[i]);
|
||||
fatent->nr_bhs = 0;
|
||||
fatent->bhs[0] = fatent->bhs[1] = NULL;
|
||||
}
|
||||
|
||||
extern void fat_ent_access_init(struct super_block *sb);
|
||||
extern int fat_ent_read(struct inode *inode, struct fat_entry *fatent,
|
||||
int entry);
|
||||
extern int fat_ent_write(struct inode *inode, struct fat_entry *fatent,
|
||||
int new, int wait);
|
||||
extern int fat_alloc_clusters(struct inode *inode, int *cluster,
|
||||
int nr_cluster);
|
||||
extern int fat_free_clusters(struct inode *inode, int cluster);
|
||||
extern int fat_count_free_clusters(struct super_block *sb);
|
||||
|
||||
/* fat/file.c */
|
||||
extern int fat_generic_ioctl(struct inode *inode, struct file *filp,
|
||||
unsigned int cmd, unsigned long arg);
|
||||
extern const struct file_operations fat_file_operations;
|
||||
extern const struct inode_operations fat_file_inode_operations;
|
||||
extern int fat_setattr(struct dentry * dentry, struct iattr * attr);
|
||||
extern void fat_truncate(struct inode *inode);
|
||||
extern int fat_getattr(struct vfsmount *mnt, struct dentry *dentry,
|
||||
struct kstat *stat);
|
||||
|
||||
/* fat/inode.c */
|
||||
extern void fat_attach(struct inode *inode, loff_t i_pos);
|
||||
extern void fat_detach(struct inode *inode);
|
||||
extern struct inode *fat_iget(struct super_block *sb, loff_t i_pos);
|
||||
extern struct inode *fat_build_inode(struct super_block *sb,
|
||||
struct msdos_dir_entry *de, loff_t i_pos);
|
||||
extern int fat_sync_inode(struct inode *inode);
|
||||
extern int fat_fill_super(struct super_block *sb, void *data, int silent,
|
||||
const struct inode_operations *fs_dir_inode_ops, int isvfat);
|
||||
|
||||
extern int fat_flush_inodes(struct super_block *sb, struct inode *i1,
|
||||
struct inode *i2);
|
||||
/* fat/misc.c */
|
||||
extern void fat_fs_panic(struct super_block *s, const char *fmt, ...)
|
||||
__attribute__ ((format (printf, 2, 3))) __cold;
|
||||
extern void fat_clusters_flush(struct super_block *sb);
|
||||
extern int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster);
|
||||
extern void fat_time_fat2unix(struct msdos_sb_info *sbi, struct timespec *ts,
|
||||
__le16 __time, __le16 __date, u8 time_cs);
|
||||
extern void fat_time_unix2fat(struct msdos_sb_info *sbi, struct timespec *ts,
|
||||
__le16 *time, __le16 *date, u8 *time_cs);
|
||||
extern int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs);
|
||||
|
||||
int fat_cache_init(void);
|
||||
void fat_cache_destroy(void);
|
||||
|
||||
/* helper for printk */
|
||||
typedef unsigned long long llu;
|
||||
|
||||
#endif /* !_FAT_H */
|
||||
|
|
@ -7,6 +7,7 @@
|
|||
#include <linux/fs.h>
|
||||
#include <linux/msdos_fs.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include "fat.h"
|
||||
|
||||
struct fatent_operations {
|
||||
void (*ent_blocknr)(struct super_block *, int, int *, sector_t *);
|
||||
|
|
@ -92,8 +93,7 @@ static int fat12_ent_bread(struct super_block *sb, struct fat_entry *fatent,
|
|||
err_brelse:
|
||||
brelse(bhs[0]);
|
||||
err:
|
||||
printk(KERN_ERR "FAT: FAT read failed (blocknr %llu)\n",
|
||||
(unsigned long long)blocknr);
|
||||
printk(KERN_ERR "FAT: FAT read failed (blocknr %llu)\n", (llu)blocknr);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
|
@ -106,7 +106,7 @@ static int fat_ent_bread(struct super_block *sb, struct fat_entry *fatent,
|
|||
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);
|
||||
(llu)blocknr);
|
||||
return -EIO;
|
||||
}
|
||||
fatent->nr_bhs = 1;
|
||||
|
|
@ -316,10 +316,20 @@ static inline int fat_ent_update_ptr(struct super_block *sb,
|
|||
/* 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;
|
||||
if (sbi->fat_bits == 12) {
|
||||
if ((offset + 1) < sb->s_blocksize) {
|
||||
/* This entry is on bhs[0]. */
|
||||
if (fatent->nr_bhs == 2) {
|
||||
brelse(bhs[1]);
|
||||
fatent->nr_bhs = 1;
|
||||
}
|
||||
} else {
|
||||
/* This entry needs the next block. */
|
||||
if (fatent->nr_bhs != 2)
|
||||
return 0;
|
||||
if (bhs[1]->b_blocknr != (blocknr + 1))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
ops->ent_set_ptr(fatent, offset);
|
||||
return 1;
|
||||
|
|
|
|||
|
|
@ -10,13 +10,13 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/msdos_fs.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/writeback.h>
|
||||
#include <linux/backing-dev.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/fsnotify.h>
|
||||
#include <linux/security.h>
|
||||
#include "fat.h"
|
||||
|
||||
int fat_generic_ioctl(struct inode *inode, struct file *filp,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
|
|
@ -29,10 +29,9 @@ int fat_generic_ioctl(struct inode *inode, struct file *filp,
|
|||
{
|
||||
u32 attr;
|
||||
|
||||
if (inode->i_ino == MSDOS_ROOT_INO)
|
||||
attr = ATTR_DIR;
|
||||
else
|
||||
attr = fat_attr(inode);
|
||||
mutex_lock(&inode->i_mutex);
|
||||
attr = fat_make_attrs(inode);
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
|
||||
return put_user(attr, user_attr);
|
||||
}
|
||||
|
|
@ -62,20 +61,16 @@ int fat_generic_ioctl(struct inode *inode, struct file *filp,
|
|||
/* Merge in ATTR_VOLUME and ATTR_DIR */
|
||||
attr |= (MSDOS_I(inode)->i_attrs & ATTR_VOLUME) |
|
||||
(is_dir ? ATTR_DIR : 0);
|
||||
oldattr = fat_attr(inode);
|
||||
oldattr = fat_make_attrs(inode);
|
||||
|
||||
/* Equivalent to a chmod() */
|
||||
ia.ia_valid = ATTR_MODE | ATTR_CTIME;
|
||||
ia.ia_ctime = current_fs_time(inode->i_sb);
|
||||
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;
|
||||
if (is_dir)
|
||||
ia.ia_mode = fat_make_mode(sbi, attr, S_IRWXUGO);
|
||||
else {
|
||||
ia.ia_mode = fat_make_mode(sbi, attr,
|
||||
S_IRUGO | S_IWUGO | (inode->i_mode & S_IXUGO));
|
||||
}
|
||||
|
||||
/* The root directory has no attributes */
|
||||
|
|
@ -115,7 +110,7 @@ int fat_generic_ioctl(struct inode *inode, struct file *filp,
|
|||
inode->i_flags &= S_IMMUTABLE;
|
||||
}
|
||||
|
||||
MSDOS_I(inode)->i_attrs = attr & ATTR_UNUSED;
|
||||
fat_save_attrs(inode, attr);
|
||||
mark_inode_dirty(inode);
|
||||
up:
|
||||
mnt_drop_write(filp->f_path.mnt);
|
||||
|
|
@ -274,7 +269,7 @@ static int fat_sanitize_mode(const struct msdos_sb_info *sbi,
|
|||
|
||||
/*
|
||||
* Note, the basic check is already done by a caller of
|
||||
* (attr->ia_mode & ~MSDOS_VALID_MODE)
|
||||
* (attr->ia_mode & ~FAT_VALID_MODE)
|
||||
*/
|
||||
|
||||
if (S_ISREG(inode->i_mode))
|
||||
|
|
@ -287,11 +282,18 @@ static int fat_sanitize_mode(const struct msdos_sb_info *sbi,
|
|||
/*
|
||||
* Of the r and x bits, all (subject to umask) must be present. Of the
|
||||
* w bits, either all (subject to umask) or none must be present.
|
||||
*
|
||||
* If fat_mode_can_hold_ro(inode) is false, can't change w bits.
|
||||
*/
|
||||
if ((perm & (S_IRUGO | S_IXUGO)) != (inode->i_mode & (S_IRUGO|S_IXUGO)))
|
||||
return -EPERM;
|
||||
if ((perm & S_IWUGO) && ((perm & S_IWUGO) != (S_IWUGO & ~mask)))
|
||||
return -EPERM;
|
||||
if (fat_mode_can_hold_ro(inode)) {
|
||||
if ((perm & S_IWUGO) && ((perm & S_IWUGO) != (S_IWUGO & ~mask)))
|
||||
return -EPERM;
|
||||
} else {
|
||||
if ((perm & S_IWUGO) != (S_IWUGO & ~mask))
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
*mode_ptr &= S_IFMT | perm;
|
||||
|
||||
|
|
@ -314,13 +316,15 @@ static int fat_allow_set_time(struct msdos_sb_info *sbi, struct inode *inode)
|
|||
}
|
||||
|
||||
#define TIMES_SET_FLAGS (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)
|
||||
/* valid file mode bits */
|
||||
#define FAT_VALID_MODE (S_IFREG | S_IFDIR | S_IRWXUGO)
|
||||
|
||||
int fat_setattr(struct dentry *dentry, struct iattr *attr)
|
||||
{
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb);
|
||||
struct inode *inode = dentry->d_inode;
|
||||
int error = 0;
|
||||
unsigned int ia_valid;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* Expand the file. Since inode_setattr() updates ->i_size
|
||||
|
|
@ -356,7 +360,7 @@ int fat_setattr(struct dentry *dentry, struct iattr *attr)
|
|||
((attr->ia_valid & ATTR_GID) &&
|
||||
(attr->ia_gid != sbi->options.fs_gid)) ||
|
||||
((attr->ia_valid & ATTR_MODE) &&
|
||||
(attr->ia_mode & ~MSDOS_VALID_MODE)))
|
||||
(attr->ia_mode & ~FAT_VALID_MODE)))
|
||||
error = -EPERM;
|
||||
|
||||
if (error) {
|
||||
|
|
@ -374,7 +378,8 @@ int fat_setattr(struct dentry *dentry, struct iattr *attr)
|
|||
attr->ia_valid &= ~ATTR_MODE;
|
||||
}
|
||||
|
||||
error = inode_setattr(inode, attr);
|
||||
if (attr->ia_valid)
|
||||
error = inode_setattr(inode, attr);
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
|
|
|||
131
fs/fat/inode.c
131
fs/fat/inode.c
|
|
@ -16,7 +16,6 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/msdos_fs.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/mpage.h>
|
||||
#include <linux/buffer_head.h>
|
||||
|
|
@ -27,7 +26,9 @@
|
|||
#include <linux/uio.h>
|
||||
#include <linux/writeback.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/hash.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include "fat.h"
|
||||
|
||||
#ifndef CONFIG_FAT_DEFAULT_IOCHARSET
|
||||
/* if user don't select VFAT, this is undefined. */
|
||||
|
|
@ -63,7 +64,7 @@ static inline int __fat_get_block(struct inode *inode, sector_t iblock,
|
|||
sector_t phys;
|
||||
int err, offset;
|
||||
|
||||
err = fat_bmap(inode, iblock, &phys, &mapped_blocks);
|
||||
err = fat_bmap(inode, iblock, &phys, &mapped_blocks, create);
|
||||
if (err)
|
||||
return err;
|
||||
if (phys) {
|
||||
|
|
@ -93,7 +94,7 @@ static inline int __fat_get_block(struct inode *inode, sector_t iblock,
|
|||
*max_blocks = min(mapped_blocks, *max_blocks);
|
||||
MSDOS_I(inode)->mmu_private += *max_blocks << sb->s_blocksize_bits;
|
||||
|
||||
err = fat_bmap(inode, iblock, &phys, &mapped_blocks);
|
||||
err = fat_bmap(inode, iblock, &phys, &mapped_blocks, create);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
|
@ -198,7 +199,14 @@ static ssize_t fat_direct_IO(int rw, struct kiocb *iocb,
|
|||
|
||||
static sector_t _fat_bmap(struct address_space *mapping, sector_t block)
|
||||
{
|
||||
return generic_block_bmap(mapping, block, fat_get_block);
|
||||
sector_t blocknr;
|
||||
|
||||
/* fat_get_cluster() assumes the requested blocknr isn't truncated. */
|
||||
mutex_lock(&mapping->host->i_mutex);
|
||||
blocknr = generic_block_bmap(mapping, block, fat_get_block);
|
||||
mutex_unlock(&mapping->host->i_mutex);
|
||||
|
||||
return blocknr;
|
||||
}
|
||||
|
||||
static const struct address_space_operations fat_aops = {
|
||||
|
|
@ -247,25 +255,21 @@ static void fat_hash_init(struct super_block *sb)
|
|||
INIT_HLIST_HEAD(&sbi->inode_hashtable[i]);
|
||||
}
|
||||
|
||||
static inline unsigned long fat_hash(struct super_block *sb, loff_t i_pos)
|
||||
static inline unsigned long fat_hash(loff_t i_pos)
|
||||
{
|
||||
unsigned long tmp = (unsigned long)i_pos | (unsigned long) sb;
|
||||
tmp = tmp + (tmp >> FAT_HASH_BITS) + (tmp >> FAT_HASH_BITS * 2);
|
||||
return tmp & FAT_HASH_MASK;
|
||||
return hash_32(i_pos, FAT_HASH_BITS);
|
||||
}
|
||||
|
||||
void fat_attach(struct inode *inode, loff_t i_pos)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(sb);
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
|
||||
struct hlist_head *head = sbi->inode_hashtable + fat_hash(i_pos);
|
||||
|
||||
spin_lock(&sbi->inode_hash_lock);
|
||||
MSDOS_I(inode)->i_pos = i_pos;
|
||||
hlist_add_head(&MSDOS_I(inode)->i_fat_hash,
|
||||
sbi->inode_hashtable + fat_hash(sb, i_pos));
|
||||
hlist_add_head(&MSDOS_I(inode)->i_fat_hash, head);
|
||||
spin_unlock(&sbi->inode_hash_lock);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(fat_attach);
|
||||
|
||||
void fat_detach(struct inode *inode)
|
||||
|
|
@ -276,13 +280,12 @@ void fat_detach(struct inode *inode)
|
|||
hlist_del_init(&MSDOS_I(inode)->i_fat_hash);
|
||||
spin_unlock(&sbi->inode_hash_lock);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(fat_detach);
|
||||
|
||||
struct inode *fat_iget(struct super_block *sb, loff_t i_pos)
|
||||
{
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(sb);
|
||||
struct hlist_head *head = sbi->inode_hashtable + fat_hash(sb, i_pos);
|
||||
struct hlist_head *head = sbi->inode_hashtable + fat_hash(i_pos);
|
||||
struct hlist_node *_p;
|
||||
struct msdos_inode_info *i;
|
||||
struct inode *inode = NULL;
|
||||
|
|
@ -341,8 +344,7 @@ static int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de)
|
|||
|
||||
if ((de->attr & ATTR_DIR) && !IS_FREE(de->name)) {
|
||||
inode->i_generation &= ~1;
|
||||
inode->i_mode = MSDOS_MKMODE(de->attr,
|
||||
S_IRWXUGO & ~sbi->options.fs_dmask) | S_IFDIR;
|
||||
inode->i_mode = fat_make_mode(sbi, de->attr, S_IRWXUGO);
|
||||
inode->i_op = sbi->dir_ops;
|
||||
inode->i_fop = &fat_dir_operations;
|
||||
|
||||
|
|
@ -359,10 +361,9 @@ static int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de)
|
|||
inode->i_nlink = fat_subdirs(inode);
|
||||
} else { /* not a directory */
|
||||
inode->i_generation |= 1;
|
||||
inode->i_mode = MSDOS_MKMODE(de->attr,
|
||||
((sbi->options.showexec && !is_exec(de->name + 8))
|
||||
? S_IRUGO|S_IWUGO : S_IRWXUGO)
|
||||
& ~sbi->options.fs_fmask) | S_IFREG;
|
||||
inode->i_mode = fat_make_mode(sbi, de->attr,
|
||||
((sbi->options.showexec && !is_exec(de->name + 8))
|
||||
? S_IRUGO|S_IWUGO : S_IRWXUGO));
|
||||
MSDOS_I(inode)->i_start = le16_to_cpu(de->start);
|
||||
if (sbi->fat_bits == 32)
|
||||
MSDOS_I(inode)->i_start |= (le16_to_cpu(de->starthi) << 16);
|
||||
|
|
@ -378,25 +379,16 @@ static int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de)
|
|||
if (sbi->options.sys_immutable)
|
||||
inode->i_flags |= S_IMMUTABLE;
|
||||
}
|
||||
MSDOS_I(inode)->i_attrs = de->attr & ATTR_UNUSED;
|
||||
fat_save_attrs(inode, de->attr);
|
||||
|
||||
inode->i_blocks = ((inode->i_size + (sbi->cluster_size - 1))
|
||||
& ~((loff_t)sbi->cluster_size - 1)) >> 9;
|
||||
inode->i_mtime.tv_sec =
|
||||
date_dos2unix(le16_to_cpu(de->time), le16_to_cpu(de->date),
|
||||
sbi->options.tz_utc);
|
||||
inode->i_mtime.tv_nsec = 0;
|
||||
|
||||
fat_time_fat2unix(sbi, &inode->i_mtime, de->time, de->date, 0);
|
||||
if (sbi->options.isvfat) {
|
||||
int secs = de->ctime_cs / 100;
|
||||
int csecs = de->ctime_cs % 100;
|
||||
inode->i_ctime.tv_sec =
|
||||
date_dos2unix(le16_to_cpu(de->ctime),
|
||||
le16_to_cpu(de->cdate),
|
||||
sbi->options.tz_utc) + secs;
|
||||
inode->i_ctime.tv_nsec = csecs * 10000000;
|
||||
inode->i_atime.tv_sec =
|
||||
date_dos2unix(0, le16_to_cpu(de->adate),
|
||||
sbi->options.tz_utc);
|
||||
inode->i_atime.tv_nsec = 0;
|
||||
fat_time_fat2unix(sbi, &inode->i_ctime, de->ctime,
|
||||
de->cdate, de->ctime_cs);
|
||||
fat_time_fat2unix(sbi, &inode->i_atime, 0, de->adate, 0);
|
||||
} else
|
||||
inode->i_ctime = inode->i_atime = inode->i_mtime;
|
||||
|
||||
|
|
@ -443,13 +435,8 @@ static void fat_delete_inode(struct inode *inode)
|
|||
|
||||
static void fat_clear_inode(struct inode *inode)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(sb);
|
||||
|
||||
spin_lock(&sbi->inode_hash_lock);
|
||||
fat_cache_inval_inode(inode);
|
||||
hlist_del_init(&MSDOS_I(inode)->i_fat_hash);
|
||||
spin_unlock(&sbi->inode_hash_lock);
|
||||
fat_detach(inode);
|
||||
}
|
||||
|
||||
static void fat_write_super(struct super_block *sb)
|
||||
|
|
@ -555,6 +542,20 @@ static int fat_statfs(struct dentry *dentry, struct kstatfs *buf)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline loff_t fat_i_pos_read(struct msdos_sb_info *sbi,
|
||||
struct inode *inode)
|
||||
{
|
||||
loff_t i_pos;
|
||||
#if BITS_PER_LONG == 32
|
||||
spin_lock(&sbi->inode_hash_lock);
|
||||
#endif
|
||||
i_pos = MSDOS_I(inode)->i_pos;
|
||||
#if BITS_PER_LONG == 32
|
||||
spin_unlock(&sbi->inode_hash_lock);
|
||||
#endif
|
||||
return i_pos;
|
||||
}
|
||||
|
||||
static int fat_write_inode(struct inode *inode, int wait)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
|
|
@ -564,9 +565,12 @@ static int fat_write_inode(struct inode *inode, int wait)
|
|||
loff_t i_pos;
|
||||
int err;
|
||||
|
||||
if (inode->i_ino == MSDOS_ROOT_INO)
|
||||
return 0;
|
||||
|
||||
retry:
|
||||
i_pos = MSDOS_I(inode)->i_pos;
|
||||
if (inode->i_ino == MSDOS_ROOT_INO || !i_pos)
|
||||
i_pos = fat_i_pos_read(sbi, inode);
|
||||
if (!i_pos)
|
||||
return 0;
|
||||
|
||||
bh = sb_bread(sb, i_pos >> sbi->dir_per_block_bits);
|
||||
|
|
@ -588,19 +592,17 @@ retry:
|
|||
raw_entry->size = 0;
|
||||
else
|
||||
raw_entry->size = cpu_to_le32(inode->i_size);
|
||||
raw_entry->attr = fat_attr(inode);
|
||||
raw_entry->attr = fat_make_attrs(inode);
|
||||
raw_entry->start = cpu_to_le16(MSDOS_I(inode)->i_logstart);
|
||||
raw_entry->starthi = cpu_to_le16(MSDOS_I(inode)->i_logstart >> 16);
|
||||
fat_date_unix2dos(inode->i_mtime.tv_sec, &raw_entry->time,
|
||||
&raw_entry->date, sbi->options.tz_utc);
|
||||
fat_time_unix2fat(sbi, &inode->i_mtime, &raw_entry->time,
|
||||
&raw_entry->date, NULL);
|
||||
if (sbi->options.isvfat) {
|
||||
__le16 atime;
|
||||
fat_date_unix2dos(inode->i_ctime.tv_sec, &raw_entry->ctime,
|
||||
&raw_entry->cdate, sbi->options.tz_utc);
|
||||
fat_date_unix2dos(inode->i_atime.tv_sec, &atime,
|
||||
&raw_entry->adate, sbi->options.tz_utc);
|
||||
raw_entry->ctime_cs = (inode->i_ctime.tv_sec & 1) * 100 +
|
||||
inode->i_ctime.tv_nsec / 10000000;
|
||||
fat_time_unix2fat(sbi, &inode->i_ctime, &raw_entry->ctime,
|
||||
&raw_entry->cdate, &raw_entry->ctime_cs);
|
||||
fat_time_unix2fat(sbi, &inode->i_atime, &atime,
|
||||
&raw_entry->adate, NULL);
|
||||
}
|
||||
spin_unlock(&sbi->inode_hash_lock);
|
||||
mark_buffer_dirty(bh);
|
||||
|
|
@ -819,8 +821,10 @@ static int fat_show_options(struct seq_file *m, struct vfsmount *mnt)
|
|||
seq_puts(m, ",uni_xlate");
|
||||
if (!opts->numtail)
|
||||
seq_puts(m, ",nonumtail");
|
||||
if (opts->rodir)
|
||||
seq_puts(m, ",rodir");
|
||||
}
|
||||
if (sbi->options.flush)
|
||||
if (opts->flush)
|
||||
seq_puts(m, ",flush");
|
||||
if (opts->tz_utc)
|
||||
seq_puts(m, ",tz=UTC");
|
||||
|
|
@ -836,7 +840,7 @@ enum {
|
|||
Opt_charset, Opt_shortname_lower, Opt_shortname_win95,
|
||||
Opt_shortname_winnt, Opt_shortname_mixed, Opt_utf8_no, Opt_utf8_yes,
|
||||
Opt_uni_xl_no, Opt_uni_xl_yes, Opt_nonumtail_no, Opt_nonumtail_yes,
|
||||
Opt_obsolate, Opt_flush, Opt_tz_utc, Opt_err,
|
||||
Opt_obsolate, Opt_flush, Opt_tz_utc, Opt_rodir, Opt_err,
|
||||
};
|
||||
|
||||
static const match_table_t fat_tokens = {
|
||||
|
|
@ -908,6 +912,7 @@ static const match_table_t vfat_tokens = {
|
|||
{Opt_nonumtail_yes, "nonumtail=yes"},
|
||||
{Opt_nonumtail_yes, "nonumtail=true"},
|
||||
{Opt_nonumtail_yes, "nonumtail"},
|
||||
{Opt_rodir, "rodir"},
|
||||
{Opt_err, NULL}
|
||||
};
|
||||
|
||||
|
|
@ -927,10 +932,13 @@ static int parse_options(char *options, int is_vfat, int silent, int *debug,
|
|||
opts->allow_utime = -1;
|
||||
opts->codepage = fat_default_codepage;
|
||||
opts->iocharset = fat_default_iocharset;
|
||||
if (is_vfat)
|
||||
if (is_vfat) {
|
||||
opts->shortname = VFAT_SFN_DISPLAY_LOWER|VFAT_SFN_CREATE_WIN95;
|
||||
else
|
||||
opts->rodir = 0;
|
||||
} else {
|
||||
opts->shortname = 0;
|
||||
opts->rodir = 1;
|
||||
}
|
||||
opts->name_check = 'n';
|
||||
opts->quiet = opts->showexec = opts->sys_immutable = opts->dotsOK = 0;
|
||||
opts->utf8 = opts->unicode_xlate = 0;
|
||||
|
|
@ -1081,6 +1089,9 @@ static int parse_options(char *options, int is_vfat, int silent, int *debug,
|
|||
case Opt_nonumtail_yes: /* empty or 1 or yes or true */
|
||||
opts->numtail = 0; /* negated option */
|
||||
break;
|
||||
case Opt_rodir:
|
||||
opts->rodir = 1;
|
||||
break;
|
||||
|
||||
/* obsolete mount options */
|
||||
case Opt_obsolate:
|
||||
|
|
@ -1126,7 +1137,7 @@ static int fat_read_root(struct inode *inode)
|
|||
inode->i_gid = sbi->options.fs_gid;
|
||||
inode->i_version++;
|
||||
inode->i_generation = 0;
|
||||
inode->i_mode = (S_IRWXUGO & ~sbi->options.fs_dmask) | S_IFDIR;
|
||||
inode->i_mode = fat_make_mode(sbi, ATTR_DIR, S_IRWXUGO);
|
||||
inode->i_op = sbi->dir_ops;
|
||||
inode->i_fop = &fat_dir_operations;
|
||||
if (sbi->fat_bits == 32) {
|
||||
|
|
@ -1143,7 +1154,7 @@ static int fat_read_root(struct inode *inode)
|
|||
MSDOS_I(inode)->i_logstart = 0;
|
||||
MSDOS_I(inode)->mmu_private = inode->i_size;
|
||||
|
||||
MSDOS_I(inode)->i_attrs = ATTR_NONE;
|
||||
fat_save_attrs(inode, ATTR_DIR);
|
||||
inode->i_mtime.tv_sec = inode->i_atime.tv_sec = inode->i_ctime.tv_sec = 0;
|
||||
inode->i_mtime.tv_nsec = inode->i_atime.tv_nsec = inode->i_ctime.tv_nsec = 0;
|
||||
inode->i_nlink = fat_subdirs(inode)+2;
|
||||
|
|
|
|||
153
fs/fat/misc.c
153
fs/fat/misc.c
|
|
@ -8,8 +8,8 @@
|
|||
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/msdos_fs.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include "fat.h"
|
||||
|
||||
/*
|
||||
* fat_fs_panic reports a severe file system problem and sets the file system
|
||||
|
|
@ -124,8 +124,9 @@ int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster)
|
|||
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_fs_panic(sb, "clusters badly computed (%d != %llu)",
|
||||
new_fclus,
|
||||
(llu)(inode->i_blocks >> (sbi->cluster_bits - 9)));
|
||||
fat_cache_inval_inode(inode);
|
||||
}
|
||||
inode->i_blocks += nr_cluster << (sbi->cluster_bits - 9);
|
||||
|
|
@ -135,65 +136,131 @@ int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster)
|
|||
|
||||
extern struct timezone sys_tz;
|
||||
|
||||
/*
|
||||
* The epoch of FAT timestamp is 1980.
|
||||
* : bits : value
|
||||
* date: 0 - 4: day (1 - 31)
|
||||
* date: 5 - 8: month (1 - 12)
|
||||
* date: 9 - 15: year (0 - 127) from 1980
|
||||
* time: 0 - 4: sec (0 - 29) 2sec counts
|
||||
* time: 5 - 10: min (0 - 59)
|
||||
* time: 11 - 15: hour (0 - 23)
|
||||
*/
|
||||
#define SECS_PER_MIN 60
|
||||
#define SECS_PER_HOUR (60 * 60)
|
||||
#define SECS_PER_DAY (SECS_PER_HOUR * 24)
|
||||
#define UNIX_SECS_1980 315532800L
|
||||
#if BITS_PER_LONG == 64
|
||||
#define UNIX_SECS_2108 4354819200L
|
||||
#endif
|
||||
/* days between 1.1.70 and 1.1.80 (2 leap days) */
|
||||
#define DAYS_DELTA (365 * 10 + 2)
|
||||
/* 120 (2100 - 1980) isn't leap year */
|
||||
#define YEAR_2100 120
|
||||
#define IS_LEAP_YEAR(y) (!((y) & 3) && (y) != YEAR_2100)
|
||||
|
||||
/* 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
|
||||
static time_t days_in_year[] = {
|
||||
/* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */
|
||||
0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 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 tz_utc)
|
||||
/* Convert a FAT time/date pair to a UNIX date (seconds since 1 1 70). */
|
||||
void fat_time_fat2unix(struct msdos_sb_info *sbi, struct timespec *ts,
|
||||
__le16 __time, __le16 __date, u8 time_cs)
|
||||
{
|
||||
int month, year, secs;
|
||||
u16 time = le16_to_cpu(__time), date = le16_to_cpu(__date);
|
||||
time_t second, day, leap_day, month, year;
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
if (!tz_utc)
|
||||
secs += sys_tz.tz_minuteswest*60;
|
||||
return secs;
|
||||
year = date >> 9;
|
||||
month = max(1, (date >> 5) & 0xf);
|
||||
day = max(1, date & 0x1f) - 1;
|
||||
|
||||
leap_day = (year + 3) / 4;
|
||||
if (year > YEAR_2100) /* 2100 isn't leap year */
|
||||
leap_day--;
|
||||
if (IS_LEAP_YEAR(year) && month > 2)
|
||||
leap_day++;
|
||||
|
||||
second = (time & 0x1f) << 1;
|
||||
second += ((time >> 5) & 0x3f) * SECS_PER_MIN;
|
||||
second += (time >> 11) * SECS_PER_HOUR;
|
||||
second += (year * 365 + leap_day
|
||||
+ days_in_year[month] + day
|
||||
+ DAYS_DELTA) * SECS_PER_DAY;
|
||||
|
||||
if (!sbi->options.tz_utc)
|
||||
second += sys_tz.tz_minuteswest * SECS_PER_MIN;
|
||||
|
||||
if (time_cs) {
|
||||
ts->tv_sec = second + (time_cs / 100);
|
||||
ts->tv_nsec = (time_cs % 100) * 10000000;
|
||||
} else {
|
||||
ts->tv_sec = second;
|
||||
ts->tv_nsec = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert linear UNIX date to a MS-DOS time/date pair. */
|
||||
void fat_date_unix2dos(int unix_date, __le16 *time, __le16 *date, int tz_utc)
|
||||
/* Convert linear UNIX date to a FAT time/date pair. */
|
||||
void fat_time_unix2fat(struct msdos_sb_info *sbi, struct timespec *ts,
|
||||
__le16 *time, __le16 *date, u8 *time_cs)
|
||||
{
|
||||
int day, year, nl_day, month;
|
||||
time_t second = ts->tv_sec;
|
||||
time_t day, leap_day, month, year;
|
||||
|
||||
if (!tz_utc)
|
||||
unix_date -= sys_tz.tz_minuteswest*60;
|
||||
if (!sbi->options.tz_utc)
|
||||
second -= sys_tz.tz_minuteswest * SECS_PER_MIN;
|
||||
|
||||
/* Jan 1 GMT 00:00:00 1980. But what about another time zone? */
|
||||
if (unix_date < 315532800)
|
||||
unix_date = 315532800;
|
||||
if (second < UNIX_SECS_1980) {
|
||||
*time = 0;
|
||||
*date = cpu_to_le16((0 << 9) | (1 << 5) | 1);
|
||||
if (time_cs)
|
||||
*time_cs = 0;
|
||||
return;
|
||||
}
|
||||
#if BITS_PER_LONG == 64
|
||||
if (second >= UNIX_SECS_2108) {
|
||||
*time = cpu_to_le16((23 << 11) | (59 << 5) | 29);
|
||||
*date = cpu_to_le16((127 << 9) | (12 << 5) | 31);
|
||||
if (time_cs)
|
||||
*time_cs = 199;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
*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)
|
||||
day = second / SECS_PER_DAY - DAYS_DELTA;
|
||||
year = day / 365;
|
||||
leap_day = (year + 3) / 4;
|
||||
if (year > YEAR_2100) /* 2100 isn't leap year */
|
||||
leap_day--;
|
||||
if (year * 365 + leap_day > day)
|
||||
year--;
|
||||
day -= (year+3)/4+365*year;
|
||||
if (day == 59 && !(year & 3)) {
|
||||
nl_day = day;
|
||||
leap_day = (year + 3) / 4;
|
||||
if (year > YEAR_2100) /* 2100 isn't leap year */
|
||||
leap_day--;
|
||||
day -= year * 365 + leap_day;
|
||||
|
||||
if (IS_LEAP_YEAR(year) && day == days_in_year[3]) {
|
||||
month = 2;
|
||||
} else {
|
||||
nl_day = (year & 3) || day <= 59 ? day : day-1;
|
||||
for (month = 0; month < 12; month++) {
|
||||
if (day_n[month] > nl_day)
|
||||
if (IS_LEAP_YEAR(year) && day > days_in_year[3])
|
||||
day--;
|
||||
for (month = 1; month < 12; month++) {
|
||||
if (days_in_year[month + 1] > day)
|
||||
break;
|
||||
}
|
||||
}
|
||||
*date = cpu_to_le16(nl_day-day_n[month-1]+1+(month << 5)+(year << 9));
|
||||
}
|
||||
day -= days_in_year[month];
|
||||
|
||||
EXPORT_SYMBOL_GPL(fat_date_unix2dos);
|
||||
*time = cpu_to_le16(((second / SECS_PER_HOUR) % 24) << 11
|
||||
| ((second / SECS_PER_MIN) % 60) << 5
|
||||
| (second % SECS_PER_MIN) >> 1);
|
||||
*date = cpu_to_le16((year << 9) | (month << 5) | (day + 1));
|
||||
if (time_cs)
|
||||
*time_cs = (ts->tv_sec & 1) * 100 + ts->tv_nsec / 10000000;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fat_time_unix2fat);
|
||||
|
||||
int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs)
|
||||
{
|
||||
|
|
|
|||
706
fs/fat/namei_msdos.c
Normal file
706
fs/fat/namei_msdos.c
Normal file
|
|
@ -0,0 +1,706 @@
|
|||
/*
|
||||
* linux/fs/msdos/namei.c
|
||||
*
|
||||
* Written 1992,1993 by Werner Almesberger
|
||||
* Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu>
|
||||
* Rewritten for constant inumbers 1999 by Al Viro
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include "fat.h"
|
||||
|
||||
/* Characters that are undesirable in an MS-DOS file name */
|
||||
static unsigned char bad_chars[] = "*?<>|\"";
|
||||
static unsigned char bad_if_strict[] = "+=,; ";
|
||||
|
||||
/***** Formats an MS-DOS file name. Rejects invalid names. */
|
||||
static int msdos_format_name(const unsigned char *name, int len,
|
||||
unsigned char *res, struct fat_mount_options *opts)
|
||||
/*
|
||||
* name is the proposed name, len is its length, res is
|
||||
* the resulting name, opts->name_check is either (r)elaxed,
|
||||
* (n)ormal or (s)trict, opts->dotsOK allows dots at the
|
||||
* beginning of name (for hidden files)
|
||||
*/
|
||||
{
|
||||
unsigned char *walk;
|
||||
unsigned char c;
|
||||
int space;
|
||||
|
||||
if (name[0] == '.') { /* dotfile because . and .. already done */
|
||||
if (opts->dotsOK) {
|
||||
/* Get rid of dot - test for it elsewhere */
|
||||
name++;
|
||||
len--;
|
||||
} else
|
||||
return -EINVAL;
|
||||
}
|
||||
/*
|
||||
* disallow names that _really_ start with a dot
|
||||
*/
|
||||
space = 1;
|
||||
c = 0;
|
||||
for (walk = res; len && walk - res < 8; walk++) {
|
||||
c = *name++;
|
||||
len--;
|
||||
if (opts->name_check != 'r' && strchr(bad_chars, c))
|
||||
return -EINVAL;
|
||||
if (opts->name_check == 's' && strchr(bad_if_strict, c))
|
||||
return -EINVAL;
|
||||
if (c >= 'A' && c <= 'Z' && opts->name_check == 's')
|
||||
return -EINVAL;
|
||||
if (c < ' ' || c == ':' || c == '\\')
|
||||
return -EINVAL;
|
||||
/*
|
||||
* 0xE5 is legal as a first character, but we must substitute
|
||||
* 0x05 because 0xE5 marks deleted files. Yes, DOS really
|
||||
* does this.
|
||||
* It seems that Microsoft hacked DOS to support non-US
|
||||
* characters after the 0xE5 character was already in use to
|
||||
* mark deleted files.
|
||||
*/
|
||||
if ((res == walk) && (c == 0xE5))
|
||||
c = 0x05;
|
||||
if (c == '.')
|
||||
break;
|
||||
space = (c == ' ');
|
||||
*walk = (!opts->nocase && c >= 'a' && c <= 'z') ? c - 32 : c;
|
||||
}
|
||||
if (space)
|
||||
return -EINVAL;
|
||||
if (opts->name_check == 's' && len && c != '.') {
|
||||
c = *name++;
|
||||
len--;
|
||||
if (c != '.')
|
||||
return -EINVAL;
|
||||
}
|
||||
while (c != '.' && len--)
|
||||
c = *name++;
|
||||
if (c == '.') {
|
||||
while (walk - res < 8)
|
||||
*walk++ = ' ';
|
||||
while (len > 0 && walk - res < MSDOS_NAME) {
|
||||
c = *name++;
|
||||
len--;
|
||||
if (opts->name_check != 'r' && strchr(bad_chars, c))
|
||||
return -EINVAL;
|
||||
if (opts->name_check == 's' &&
|
||||
strchr(bad_if_strict, c))
|
||||
return -EINVAL;
|
||||
if (c < ' ' || c == ':' || c == '\\')
|
||||
return -EINVAL;
|
||||
if (c == '.') {
|
||||
if (opts->name_check == 's')
|
||||
return -EINVAL;
|
||||
break;
|
||||
}
|
||||
if (c >= 'A' && c <= 'Z' && opts->name_check == 's')
|
||||
return -EINVAL;
|
||||
space = c == ' ';
|
||||
if (!opts->nocase && c >= 'a' && c <= 'z')
|
||||
*walk++ = c - 32;
|
||||
else
|
||||
*walk++ = c;
|
||||
}
|
||||
if (space)
|
||||
return -EINVAL;
|
||||
if (opts->name_check == 's' && len)
|
||||
return -EINVAL;
|
||||
}
|
||||
while (walk - res < MSDOS_NAME)
|
||||
*walk++ = ' ';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***** Locates a directory entry. Uses unformatted name. */
|
||||
static int msdos_find(struct inode *dir, const unsigned char *name, int len,
|
||||
struct fat_slot_info *sinfo)
|
||||
{
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb);
|
||||
unsigned char msdos_name[MSDOS_NAME];
|
||||
int err;
|
||||
|
||||
err = msdos_format_name(name, len, msdos_name, &sbi->options);
|
||||
if (err)
|
||||
return -ENOENT;
|
||||
|
||||
err = fat_scan(dir, msdos_name, sinfo);
|
||||
if (!err && sbi->options.dotsOK) {
|
||||
if (name[0] == '.') {
|
||||
if (!(sinfo->de->attr & ATTR_HIDDEN))
|
||||
err = -ENOENT;
|
||||
} else {
|
||||
if (sinfo->de->attr & ATTR_HIDDEN)
|
||||
err = -ENOENT;
|
||||
}
|
||||
if (err)
|
||||
brelse(sinfo->bh);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute the hash for the msdos name corresponding to the dentry.
|
||||
* Note: if the name is invalid, we leave the hash code unchanged so
|
||||
* that the existing dentry can be used. The msdos fs routines will
|
||||
* return ENOENT or EINVAL as appropriate.
|
||||
*/
|
||||
static int msdos_hash(struct dentry *dentry, struct qstr *qstr)
|
||||
{
|
||||
struct fat_mount_options *options = &MSDOS_SB(dentry->d_sb)->options;
|
||||
unsigned char msdos_name[MSDOS_NAME];
|
||||
int error;
|
||||
|
||||
error = msdos_format_name(qstr->name, qstr->len, msdos_name, options);
|
||||
if (!error)
|
||||
qstr->hash = full_name_hash(msdos_name, MSDOS_NAME);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare two msdos names. If either of the names are invalid,
|
||||
* we fall back to doing the standard name comparison.
|
||||
*/
|
||||
static int msdos_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b)
|
||||
{
|
||||
struct fat_mount_options *options = &MSDOS_SB(dentry->d_sb)->options;
|
||||
unsigned char a_msdos_name[MSDOS_NAME], b_msdos_name[MSDOS_NAME];
|
||||
int error;
|
||||
|
||||
error = msdos_format_name(a->name, a->len, a_msdos_name, options);
|
||||
if (error)
|
||||
goto old_compare;
|
||||
error = msdos_format_name(b->name, b->len, b_msdos_name, options);
|
||||
if (error)
|
||||
goto old_compare;
|
||||
error = memcmp(a_msdos_name, b_msdos_name, MSDOS_NAME);
|
||||
out:
|
||||
return error;
|
||||
|
||||
old_compare:
|
||||
error = 1;
|
||||
if (a->len == b->len)
|
||||
error = memcmp(a->name, b->name, a->len);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static struct dentry_operations msdos_dentry_operations = {
|
||||
.d_hash = msdos_hash,
|
||||
.d_compare = msdos_cmp,
|
||||
};
|
||||
|
||||
/*
|
||||
* AV. Wrappers for FAT sb operations. Is it wise?
|
||||
*/
|
||||
|
||||
/***** Get inode using directory and name */
|
||||
static struct dentry *msdos_lookup(struct inode *dir, struct dentry *dentry,
|
||||
struct nameidata *nd)
|
||||
{
|
||||
struct super_block *sb = dir->i_sb;
|
||||
struct fat_slot_info sinfo;
|
||||
struct inode *inode;
|
||||
int err;
|
||||
|
||||
lock_super(sb);
|
||||
|
||||
err = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &sinfo);
|
||||
if (err) {
|
||||
if (err == -ENOENT) {
|
||||
inode = NULL;
|
||||
goto out;
|
||||
}
|
||||
goto error;
|
||||
}
|
||||
|
||||
inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
|
||||
brelse(sinfo.bh);
|
||||
if (IS_ERR(inode)) {
|
||||
err = PTR_ERR(inode);
|
||||
goto error;
|
||||
}
|
||||
out:
|
||||
unlock_super(sb);
|
||||
dentry->d_op = &msdos_dentry_operations;
|
||||
dentry = d_splice_alias(inode, dentry);
|
||||
if (dentry)
|
||||
dentry->d_op = &msdos_dentry_operations;
|
||||
return dentry;
|
||||
|
||||
error:
|
||||
unlock_super(sb);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
/***** Creates a directory entry (name is already formatted). */
|
||||
static int msdos_add_entry(struct inode *dir, const unsigned char *name,
|
||||
int is_dir, int is_hid, int cluster,
|
||||
struct timespec *ts, struct fat_slot_info *sinfo)
|
||||
{
|
||||
struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb);
|
||||
struct msdos_dir_entry de;
|
||||
__le16 time, date;
|
||||
int err;
|
||||
|
||||
memcpy(de.name, name, MSDOS_NAME);
|
||||
de.attr = is_dir ? ATTR_DIR : ATTR_ARCH;
|
||||
if (is_hid)
|
||||
de.attr |= ATTR_HIDDEN;
|
||||
de.lcase = 0;
|
||||
fat_time_unix2fat(sbi, ts, &time, &date, NULL);
|
||||
de.cdate = de.adate = 0;
|
||||
de.ctime = 0;
|
||||
de.ctime_cs = 0;
|
||||
de.time = time;
|
||||
de.date = date;
|
||||
de.start = cpu_to_le16(cluster);
|
||||
de.starthi = cpu_to_le16(cluster >> 16);
|
||||
de.size = 0;
|
||||
|
||||
err = fat_add_entries(dir, &de, 1, sinfo);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
dir->i_ctime = dir->i_mtime = *ts;
|
||||
if (IS_DIRSYNC(dir))
|
||||
(void)fat_sync_inode(dir);
|
||||
else
|
||||
mark_inode_dirty(dir);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***** Create a file */
|
||||
static int msdos_create(struct inode *dir, struct dentry *dentry, int mode,
|
||||
struct nameidata *nd)
|
||||
{
|
||||
struct super_block *sb = dir->i_sb;
|
||||
struct inode *inode = NULL;
|
||||
struct fat_slot_info sinfo;
|
||||
struct timespec ts;
|
||||
unsigned char msdos_name[MSDOS_NAME];
|
||||
int err, is_hid;
|
||||
|
||||
lock_super(sb);
|
||||
|
||||
err = msdos_format_name(dentry->d_name.name, dentry->d_name.len,
|
||||
msdos_name, &MSDOS_SB(sb)->options);
|
||||
if (err)
|
||||
goto out;
|
||||
is_hid = (dentry->d_name.name[0] == '.') && (msdos_name[0] != '.');
|
||||
/* Have to do it due to foo vs. .foo conflicts */
|
||||
if (!fat_scan(dir, msdos_name, &sinfo)) {
|
||||
brelse(sinfo.bh);
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ts = CURRENT_TIME_SEC;
|
||||
err = msdos_add_entry(dir, msdos_name, 0, is_hid, 0, &ts, &sinfo);
|
||||
if (err)
|
||||
goto out;
|
||||
inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
|
||||
brelse(sinfo.bh);
|
||||
if (IS_ERR(inode)) {
|
||||
err = PTR_ERR(inode);
|
||||
goto out;
|
||||
}
|
||||
inode->i_mtime = inode->i_atime = inode->i_ctime = ts;
|
||||
/* timestamp is already written, so mark_inode_dirty() is unneeded. */
|
||||
|
||||
d_instantiate(dentry, inode);
|
||||
out:
|
||||
unlock_super(sb);
|
||||
if (!err)
|
||||
err = fat_flush_inodes(sb, dir, inode);
|
||||
return err;
|
||||
}
|
||||
|
||||
/***** Remove a directory */
|
||||
static int msdos_rmdir(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
struct super_block *sb = dir->i_sb;
|
||||
struct inode *inode = dentry->d_inode;
|
||||
struct fat_slot_info sinfo;
|
||||
int err;
|
||||
|
||||
lock_super(sb);
|
||||
/*
|
||||
* Check whether the directory is not in use, then check
|
||||
* whether it is empty.
|
||||
*/
|
||||
err = fat_dir_empty(inode);
|
||||
if (err)
|
||||
goto out;
|
||||
err = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &sinfo);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = fat_remove_entries(dir, &sinfo); /* and releases bh */
|
||||
if (err)
|
||||
goto out;
|
||||
drop_nlink(dir);
|
||||
|
||||
clear_nlink(inode);
|
||||
inode->i_ctime = CURRENT_TIME_SEC;
|
||||
fat_detach(inode);
|
||||
out:
|
||||
unlock_super(sb);
|
||||
if (!err)
|
||||
err = fat_flush_inodes(sb, dir, inode);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/***** Make a directory */
|
||||
static int msdos_mkdir(struct inode *dir, struct dentry *dentry, int mode)
|
||||
{
|
||||
struct super_block *sb = dir->i_sb;
|
||||
struct fat_slot_info sinfo;
|
||||
struct inode *inode;
|
||||
unsigned char msdos_name[MSDOS_NAME];
|
||||
struct timespec ts;
|
||||
int err, is_hid, cluster;
|
||||
|
||||
lock_super(sb);
|
||||
|
||||
err = msdos_format_name(dentry->d_name.name, dentry->d_name.len,
|
||||
msdos_name, &MSDOS_SB(sb)->options);
|
||||
if (err)
|
||||
goto out;
|
||||
is_hid = (dentry->d_name.name[0] == '.') && (msdos_name[0] != '.');
|
||||
/* foo vs .foo situation */
|
||||
if (!fat_scan(dir, msdos_name, &sinfo)) {
|
||||
brelse(sinfo.bh);
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ts = CURRENT_TIME_SEC;
|
||||
cluster = fat_alloc_new_dir(dir, &ts);
|
||||
if (cluster < 0) {
|
||||
err = cluster;
|
||||
goto out;
|
||||
}
|
||||
err = msdos_add_entry(dir, msdos_name, 1, is_hid, cluster, &ts, &sinfo);
|
||||
if (err)
|
||||
goto out_free;
|
||||
inc_nlink(dir);
|
||||
|
||||
inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos);
|
||||
brelse(sinfo.bh);
|
||||
if (IS_ERR(inode)) {
|
||||
err = PTR_ERR(inode);
|
||||
/* the directory was completed, just return a error */
|
||||
goto out;
|
||||
}
|
||||
inode->i_nlink = 2;
|
||||
inode->i_mtime = inode->i_atime = inode->i_ctime = ts;
|
||||
/* timestamp is already written, so mark_inode_dirty() is unneeded. */
|
||||
|
||||
d_instantiate(dentry, inode);
|
||||
|
||||
unlock_super(sb);
|
||||
fat_flush_inodes(sb, dir, inode);
|
||||
return 0;
|
||||
|
||||
out_free:
|
||||
fat_free_clusters(dir, cluster);
|
||||
out:
|
||||
unlock_super(sb);
|
||||
return err;
|
||||
}
|
||||
|
||||
/***** Unlink a file */
|
||||
static int msdos_unlink(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
struct super_block *sb= inode->i_sb;
|
||||
struct fat_slot_info sinfo;
|
||||
int err;
|
||||
|
||||
lock_super(sb);
|
||||
err = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &sinfo);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = fat_remove_entries(dir, &sinfo); /* and releases bh */
|
||||
if (err)
|
||||
goto out;
|
||||
clear_nlink(inode);
|
||||
inode->i_ctime = CURRENT_TIME_SEC;
|
||||
fat_detach(inode);
|
||||
out:
|
||||
unlock_super(sb);
|
||||
if (!err)
|
||||
err = fat_flush_inodes(sb, dir, inode);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name,
|
||||
struct dentry *old_dentry,
|
||||
struct inode *new_dir, unsigned char *new_name,
|
||||
struct dentry *new_dentry, int is_hid)
|
||||
{
|
||||
struct buffer_head *dotdot_bh;
|
||||
struct msdos_dir_entry *dotdot_de;
|
||||
struct inode *old_inode, *new_inode;
|
||||
struct fat_slot_info old_sinfo, sinfo;
|
||||
struct timespec ts;
|
||||
loff_t dotdot_i_pos, new_i_pos;
|
||||
int err, old_attrs, is_dir, update_dotdot, corrupt = 0;
|
||||
|
||||
old_sinfo.bh = sinfo.bh = dotdot_bh = NULL;
|
||||
old_inode = old_dentry->d_inode;
|
||||
new_inode = new_dentry->d_inode;
|
||||
|
||||
err = fat_scan(old_dir, old_name, &old_sinfo);
|
||||
if (err) {
|
||||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
is_dir = S_ISDIR(old_inode->i_mode);
|
||||
update_dotdot = (is_dir && old_dir != new_dir);
|
||||
if (update_dotdot) {
|
||||
if (fat_get_dotdot_entry(old_inode, &dotdot_bh, &dotdot_de,
|
||||
&dotdot_i_pos) < 0) {
|
||||
err = -EIO;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
old_attrs = MSDOS_I(old_inode)->i_attrs;
|
||||
err = fat_scan(new_dir, new_name, &sinfo);
|
||||
if (!err) {
|
||||
if (!new_inode) {
|
||||
/* "foo" -> ".foo" case. just change the ATTR_HIDDEN */
|
||||
if (sinfo.de != old_sinfo.de) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (is_hid)
|
||||
MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
|
||||
else
|
||||
MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
|
||||
if (IS_DIRSYNC(old_dir)) {
|
||||
err = fat_sync_inode(old_inode);
|
||||
if (err) {
|
||||
MSDOS_I(old_inode)->i_attrs = old_attrs;
|
||||
goto out;
|
||||
}
|
||||
} else
|
||||
mark_inode_dirty(old_inode);
|
||||
|
||||
old_dir->i_version++;
|
||||
old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME_SEC;
|
||||
if (IS_DIRSYNC(old_dir))
|
||||
(void)fat_sync_inode(old_dir);
|
||||
else
|
||||
mark_inode_dirty(old_dir);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ts = CURRENT_TIME_SEC;
|
||||
if (new_inode) {
|
||||
if (err)
|
||||
goto out;
|
||||
if (is_dir) {
|
||||
err = fat_dir_empty(new_inode);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
new_i_pos = MSDOS_I(new_inode)->i_pos;
|
||||
fat_detach(new_inode);
|
||||
} else {
|
||||
err = msdos_add_entry(new_dir, new_name, is_dir, is_hid, 0,
|
||||
&ts, &sinfo);
|
||||
if (err)
|
||||
goto out;
|
||||
new_i_pos = sinfo.i_pos;
|
||||
}
|
||||
new_dir->i_version++;
|
||||
|
||||
fat_detach(old_inode);
|
||||
fat_attach(old_inode, new_i_pos);
|
||||
if (is_hid)
|
||||
MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
|
||||
else
|
||||
MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
|
||||
if (IS_DIRSYNC(new_dir)) {
|
||||
err = fat_sync_inode(old_inode);
|
||||
if (err)
|
||||
goto error_inode;
|
||||
} else
|
||||
mark_inode_dirty(old_inode);
|
||||
|
||||
if (update_dotdot) {
|
||||
int start = MSDOS_I(new_dir)->i_logstart;
|
||||
dotdot_de->start = cpu_to_le16(start);
|
||||
dotdot_de->starthi = cpu_to_le16(start >> 16);
|
||||
mark_buffer_dirty(dotdot_bh);
|
||||
if (IS_DIRSYNC(new_dir)) {
|
||||
err = sync_dirty_buffer(dotdot_bh);
|
||||
if (err)
|
||||
goto error_dotdot;
|
||||
}
|
||||
drop_nlink(old_dir);
|
||||
if (!new_inode)
|
||||
inc_nlink(new_dir);
|
||||
}
|
||||
|
||||
err = fat_remove_entries(old_dir, &old_sinfo); /* and releases bh */
|
||||
old_sinfo.bh = NULL;
|
||||
if (err)
|
||||
goto error_dotdot;
|
||||
old_dir->i_version++;
|
||||
old_dir->i_ctime = old_dir->i_mtime = ts;
|
||||
if (IS_DIRSYNC(old_dir))
|
||||
(void)fat_sync_inode(old_dir);
|
||||
else
|
||||
mark_inode_dirty(old_dir);
|
||||
|
||||
if (new_inode) {
|
||||
drop_nlink(new_inode);
|
||||
if (is_dir)
|
||||
drop_nlink(new_inode);
|
||||
new_inode->i_ctime = ts;
|
||||
}
|
||||
out:
|
||||
brelse(sinfo.bh);
|
||||
brelse(dotdot_bh);
|
||||
brelse(old_sinfo.bh);
|
||||
return err;
|
||||
|
||||
error_dotdot:
|
||||
/* data cluster is shared, serious corruption */
|
||||
corrupt = 1;
|
||||
|
||||
if (update_dotdot) {
|
||||
int start = MSDOS_I(old_dir)->i_logstart;
|
||||
dotdot_de->start = cpu_to_le16(start);
|
||||
dotdot_de->starthi = cpu_to_le16(start >> 16);
|
||||
mark_buffer_dirty(dotdot_bh);
|
||||
corrupt |= sync_dirty_buffer(dotdot_bh);
|
||||
}
|
||||
error_inode:
|
||||
fat_detach(old_inode);
|
||||
fat_attach(old_inode, old_sinfo.i_pos);
|
||||
MSDOS_I(old_inode)->i_attrs = old_attrs;
|
||||
if (new_inode) {
|
||||
fat_attach(new_inode, new_i_pos);
|
||||
if (corrupt)
|
||||
corrupt |= fat_sync_inode(new_inode);
|
||||
} else {
|
||||
/*
|
||||
* If new entry was not sharing the data cluster, it
|
||||
* shouldn't be serious corruption.
|
||||
*/
|
||||
int err2 = fat_remove_entries(new_dir, &sinfo);
|
||||
if (corrupt)
|
||||
corrupt |= err2;
|
||||
sinfo.bh = NULL;
|
||||
}
|
||||
if (corrupt < 0) {
|
||||
fat_fs_panic(new_dir->i_sb,
|
||||
"%s: Filesystem corrupted (i_pos %lld)",
|
||||
__func__, sinfo.i_pos);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
/***** Rename, a wrapper for rename_same_dir & rename_diff_dir */
|
||||
static int msdos_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
struct inode *new_dir, struct dentry *new_dentry)
|
||||
{
|
||||
struct super_block *sb = old_dir->i_sb;
|
||||
unsigned char old_msdos_name[MSDOS_NAME], new_msdos_name[MSDOS_NAME];
|
||||
int err, is_hid;
|
||||
|
||||
lock_super(sb);
|
||||
|
||||
err = msdos_format_name(old_dentry->d_name.name,
|
||||
old_dentry->d_name.len, old_msdos_name,
|
||||
&MSDOS_SB(old_dir->i_sb)->options);
|
||||
if (err)
|
||||
goto out;
|
||||
err = msdos_format_name(new_dentry->d_name.name,
|
||||
new_dentry->d_name.len, new_msdos_name,
|
||||
&MSDOS_SB(new_dir->i_sb)->options);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
is_hid =
|
||||
(new_dentry->d_name.name[0] == '.') && (new_msdos_name[0] != '.');
|
||||
|
||||
err = do_msdos_rename(old_dir, old_msdos_name, old_dentry,
|
||||
new_dir, new_msdos_name, new_dentry, is_hid);
|
||||
out:
|
||||
unlock_super(sb);
|
||||
if (!err)
|
||||
err = fat_flush_inodes(sb, old_dir, new_dir);
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct inode_operations msdos_dir_inode_operations = {
|
||||
.create = msdos_create,
|
||||
.lookup = msdos_lookup,
|
||||
.unlink = msdos_unlink,
|
||||
.mkdir = msdos_mkdir,
|
||||
.rmdir = msdos_rmdir,
|
||||
.rename = msdos_rename,
|
||||
.setattr = fat_setattr,
|
||||
.getattr = fat_getattr,
|
||||
};
|
||||
|
||||
static int msdos_fill_super(struct super_block *sb, void *data, int silent)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = fat_fill_super(sb, data, silent, &msdos_dir_inode_operations, 0);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
sb->s_flags |= MS_NOATIME;
|
||||
sb->s_root->d_op = &msdos_dentry_operations;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msdos_get_sb(struct file_system_type *fs_type,
|
||||
int flags, const char *dev_name,
|
||||
void *data, struct vfsmount *mnt)
|
||||
{
|
||||
return get_sb_bdev(fs_type, flags, dev_name, data, msdos_fill_super,
|
||||
mnt);
|
||||
}
|
||||
|
||||
static struct file_system_type msdos_fs_type = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "msdos",
|
||||
.get_sb = msdos_get_sb,
|
||||
.kill_sb = kill_block_super,
|
||||
.fs_flags = FS_REQUIRES_DEV,
|
||||
};
|
||||
|
||||
static int __init init_msdos_fs(void)
|
||||
{
|
||||
return register_filesystem(&msdos_fs_type);
|
||||
}
|
||||
|
||||
static void __exit exit_msdos_fs(void)
|
||||
{
|
||||
unregister_filesystem(&msdos_fs_type);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Werner Almesberger");
|
||||
MODULE_DESCRIPTION("MS-DOS filesystem support");
|
||||
|
||||
module_init(init_msdos_fs)
|
||||
module_exit(exit_msdos_fs)
|
||||
1098
fs/fat/namei_vfat.c
Normal file
1098
fs/fat/namei_vfat.c
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue