linux/fs/fat/file.c
<<
>>
Prefs
   1/*
   2 *  linux/fs/fat/file.c
   3 *
   4 *  Written 1992,1993 by Werner Almesberger
   5 *
   6 *  regular file handling primitives for fat-based filesystems
   7 */
   8
   9#include <linux/capability.h>
  10#include <linux/module.h>
  11#include <linux/time.h>
  12#include <linux/msdos_fs.h>
  13#include <linux/smp_lock.h>
  14#include <linux/buffer_head.h>
  15#include <linux/writeback.h>
  16#include <linux/backing-dev.h>
  17#include <linux/blkdev.h>
  18
  19int fat_generic_ioctl(struct inode *inode, struct file *filp,
  20                      unsigned int cmd, unsigned long arg)
  21{
  22        struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
  23        u32 __user *user_attr = (u32 __user *)arg;
  24
  25        switch (cmd) {
  26        case FAT_IOCTL_GET_ATTRIBUTES:
  27        {
  28                u32 attr;
  29
  30                if (inode->i_ino == MSDOS_ROOT_INO)
  31                        attr = ATTR_DIR;
  32                else
  33                        attr = fat_attr(inode);
  34
  35                return put_user(attr, user_attr);
  36        }
  37        case FAT_IOCTL_SET_ATTRIBUTES:
  38        {
  39                u32 attr, oldattr;
  40                int err, is_dir = S_ISDIR(inode->i_mode);
  41                struct iattr ia;
  42
  43                err = get_user(attr, user_attr);
  44                if (err)
  45                        return err;
  46
  47                mutex_lock(&inode->i_mutex);
  48
  49                if (IS_RDONLY(inode)) {
  50                        err = -EROFS;
  51                        goto up;
  52                }
  53
  54                /*
  55                 * ATTR_VOLUME and ATTR_DIR cannot be changed; this also
  56                 * prevents the user from turning us into a VFAT
  57                 * longname entry.  Also, we obviously can't set
  58                 * any of the NTFS attributes in the high 24 bits.
  59                 */
  60                attr &= 0xff & ~(ATTR_VOLUME | ATTR_DIR);
  61                /* Merge in ATTR_VOLUME and ATTR_DIR */
  62                attr |= (MSDOS_I(inode)->i_attrs & ATTR_VOLUME) |
  63                        (is_dir ? ATTR_DIR : 0);
  64                oldattr = fat_attr(inode);
  65
  66                /* Equivalent to a chmod() */
  67                ia.ia_valid = ATTR_MODE | ATTR_CTIME;
  68                if (is_dir) {
  69                        ia.ia_mode = MSDOS_MKMODE(attr,
  70                                S_IRWXUGO & ~sbi->options.fs_dmask)
  71                                | S_IFDIR;
  72                } else {
  73                        ia.ia_mode = MSDOS_MKMODE(attr,
  74                                (S_IRUGO | S_IWUGO | (inode->i_mode & S_IXUGO))
  75                                & ~sbi->options.fs_fmask)
  76                                | S_IFREG;
  77                }
  78
  79                /* The root directory has no attributes */
  80                if (inode->i_ino == MSDOS_ROOT_INO && attr != ATTR_DIR) {
  81                        err = -EINVAL;
  82                        goto up;
  83                }
  84
  85                if (sbi->options.sys_immutable) {
  86                        if ((attr | oldattr) & ATTR_SYS) {
  87                                if (!capable(CAP_LINUX_IMMUTABLE)) {
  88                                        err = -EPERM;
  89                                        goto up;
  90                                }
  91                        }
  92                }
  93
  94                /* This MUST be done before doing anything irreversible... */
  95                err = notify_change(filp->f_path.dentry, &ia);
  96                if (err)
  97                        goto up;
  98
  99                if (sbi->options.sys_immutable) {
 100                        if (attr & ATTR_SYS)
 101                                inode->i_flags |= S_IMMUTABLE;
 102                        else
 103                                inode->i_flags &= S_IMMUTABLE;
 104                }
 105
 106                MSDOS_I(inode)->i_attrs = attr & ATTR_UNUSED;
 107                mark_inode_dirty(inode);
 108        up:
 109                mutex_unlock(&inode->i_mutex);
 110                return err;
 111        }
 112        default:
 113                return -ENOTTY; /* Inappropriate ioctl for device */
 114        }
 115}
 116
 117static int fat_file_release(struct inode *inode, struct file *filp)
 118{
 119        if ((filp->f_mode & FMODE_WRITE) &&
 120             MSDOS_SB(inode->i_sb)->options.flush) {
 121                fat_flush_inodes(inode->i_sb, inode, NULL);
 122                congestion_wait(WRITE, HZ/10);
 123        }
 124        return 0;
 125}
 126
 127const struct file_operations fat_file_operations = {
 128        .llseek         = generic_file_llseek,
 129        .read           = do_sync_read,
 130        .write          = do_sync_write,
 131        .aio_read       = generic_file_aio_read,
 132        .aio_write      = generic_file_aio_write,
 133        .mmap           = generic_file_mmap,
 134        .release        = fat_file_release,
 135        .ioctl          = fat_generic_ioctl,
 136        .fsync          = file_fsync,
 137        .splice_read    = generic_file_splice_read,
 138};
 139
 140static int fat_cont_expand(struct inode *inode, loff_t size)
 141{
 142        struct address_space *mapping = inode->i_mapping;
 143        loff_t start = inode->i_size, count = size - inode->i_size;
 144        int err;
 145
 146        err = generic_cont_expand_simple(inode, size);
 147        if (err)
 148                goto out;
 149
 150        inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC;
 151        mark_inode_dirty(inode);
 152        if (IS_SYNC(inode))
 153                err = sync_page_range_nolock(inode, mapping, start, count);
 154out:
 155        return err;
 156}
 157
 158int fat_notify_change(struct dentry *dentry, struct iattr *attr)
 159{
 160        struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb);
 161        struct inode *inode = dentry->d_inode;
 162        int mask, error = 0;
 163
 164        lock_kernel();
 165
 166        /*
 167         * Expand the file. Since inode_setattr() updates ->i_size
 168         * before calling the ->truncate(), but FAT needs to fill the
 169         * hole before it.
 170         */
 171        if (attr->ia_valid & ATTR_SIZE) {
 172                if (attr->ia_size > inode->i_size) {
 173                        error = fat_cont_expand(inode, attr->ia_size);
 174                        if (error || attr->ia_valid == ATTR_SIZE)
 175                                goto out;
 176                        attr->ia_valid &= ~ATTR_SIZE;
 177                }
 178        }
 179
 180        error = inode_change_ok(inode, attr);
 181        if (error) {
 182                if (sbi->options.quiet)
 183                        error = 0;
 184                goto out;
 185        }
 186        if (((attr->ia_valid & ATTR_UID) &&
 187             (attr->ia_uid != sbi->options.fs_uid)) ||
 188            ((attr->ia_valid & ATTR_GID) &&
 189             (attr->ia_gid != sbi->options.fs_gid)) ||
 190            ((attr->ia_valid & ATTR_MODE) &&
 191             (attr->ia_mode & ~MSDOS_VALID_MODE)))
 192                error = -EPERM;
 193
 194        if (error) {
 195                if (sbi->options.quiet)
 196                        error = 0;
 197                goto out;
 198        }
 199        error = inode_setattr(inode, attr);
 200        if (error)
 201                goto out;
 202
 203        if (S_ISDIR(inode->i_mode))
 204                mask = sbi->options.fs_dmask;
 205        else
 206                mask = sbi->options.fs_fmask;
 207        inode->i_mode &= S_IFMT | (S_IRWXUGO & ~mask);
 208out:
 209        unlock_kernel();
 210        return error;
 211}
 212
 213EXPORT_SYMBOL_GPL(fat_notify_change);
 214
 215/* Free all clusters after the skip'th cluster. */
 216static int fat_free(struct inode *inode, int skip)
 217{
 218        struct super_block *sb = inode->i_sb;
 219        int err, wait, free_start, i_start, i_logstart;
 220
 221        if (MSDOS_I(inode)->i_start == 0)
 222                return 0;
 223
 224        fat_cache_inval_inode(inode);
 225
 226        wait = IS_DIRSYNC(inode);
 227        i_start = free_start = MSDOS_I(inode)->i_start;
 228        i_logstart = MSDOS_I(inode)->i_logstart;
 229
 230        /* First, we write the new file size. */
 231        if (!skip) {
 232                MSDOS_I(inode)->i_start = 0;
 233                MSDOS_I(inode)->i_logstart = 0;
 234        }
 235        MSDOS_I(inode)->i_attrs |= ATTR_ARCH;
 236        inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC;
 237        if (wait) {
 238                err = fat_sync_inode(inode);
 239                if (err) {
 240                        MSDOS_I(inode)->i_start = i_start;
 241                        MSDOS_I(inode)->i_logstart = i_logstart;
 242                        return err;
 243                }
 244        } else
 245                mark_inode_dirty(inode);
 246
 247        /* Write a new EOF, and get the remaining cluster chain for freeing. */
 248        if (skip) {
 249                struct fat_entry fatent;
 250                int ret, fclus, dclus;
 251
 252                ret = fat_get_cluster(inode, skip - 1, &fclus, &dclus);
 253                if (ret < 0)
 254                        return ret;
 255                else if (ret == FAT_ENT_EOF)
 256                        return 0;
 257
 258                fatent_init(&fatent);
 259                ret = fat_ent_read(inode, &fatent, dclus);
 260                if (ret == FAT_ENT_EOF) {
 261                        fatent_brelse(&fatent);
 262                        return 0;
 263                } else if (ret == FAT_ENT_FREE) {
 264                        fat_fs_panic(sb,
 265                                     "%s: invalid cluster chain (i_pos %lld)",
 266                                     __FUNCTION__, MSDOS_I(inode)->i_pos);
 267                        ret = -EIO;
 268                } else if (ret > 0) {
 269                        err = fat_ent_write(inode, &fatent, FAT_ENT_EOF, wait);
 270                        if (err)
 271                                ret = err;
 272                }
 273                fatent_brelse(&fatent);
 274                if (ret < 0)
 275                        return ret;
 276
 277                free_start = ret;
 278        }
 279        inode->i_blocks = skip << (MSDOS_SB(sb)->cluster_bits - 9);
 280
 281        /* Freeing the remained cluster chain */
 282        return fat_free_clusters(inode, free_start);
 283}
 284
 285void fat_truncate(struct inode *inode)
 286{
 287        struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
 288        const unsigned int cluster_size = sbi->cluster_size;
 289        int nr_clusters;
 290
 291        /*
 292         * This protects against truncating a file bigger than it was then
 293         * trying to write into the hole.
 294         */
 295        if (MSDOS_I(inode)->mmu_private > inode->i_size)
 296                MSDOS_I(inode)->mmu_private = inode->i_size;
 297
 298        nr_clusters = (inode->i_size + (cluster_size - 1)) >> sbi->cluster_bits;
 299
 300        lock_kernel();
 301        fat_free(inode, nr_clusters);
 302        unlock_kernel();
 303        fat_flush_inodes(inode->i_sb, inode, NULL);
 304}
 305
 306int fat_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
 307{
 308        struct inode *inode = dentry->d_inode;
 309        generic_fillattr(inode, stat);
 310        stat->blksize = MSDOS_SB(inode->i_sb)->cluster_size;
 311        return 0;
 312}
 313EXPORT_SYMBOL_GPL(fat_getattr);
 314
 315const struct inode_operations fat_file_inode_operations = {
 316        .truncate       = fat_truncate,
 317        .setattr        = fat_notify_change,
 318        .getattr        = fat_getattr,
 319};
 320