static long fat_fallocate(struct file *file, int mode, loff_t offset, loff_t len) { int err = 0; struct inode *inode = file->f_mapping->host; int cluster, nr_cluster, fclus, dclus, free_bytes, nr_bytes; struct super_block *sb = inode->i_sb; struct msdos_sb_info *sbi = MSDOS_SB(sb); if (mode & ~FALLOC_FL_KEEP_SIZE) return -EOPNOTSUPP; if ((offset + len) <= MSDOS_I(inode)->mmu_private) { fat_msg(sb, KERN_ERR, "fat_fallocate():Blocks already allocated"); return -EINVAL; } if ((mode & FALLOC_FL_KEEP_SIZE)) { if (inode->i_size > 0) { err = fat_get_cluster(inode, FAT_ENT_EOF, &fclus, &dclus); if (err < 0) { fat_msg(sb, KERN_ERR, "fat_fallocate():fat_get_cluster() error"); return err; } free_bytes = ((fclus+1) << sbi->cluster_bits) - (inode->i_size); nr_bytes = (offset + len - inode->i_size) - free_bytes; } else nr_bytes = (offset + len - inode->i_size); nr_cluster = (nr_bytes + (sbi->cluster_size - 1)) >> sbi->cluster_bits; mutex_lock(&inode->i_mutex); while (nr_cluster-- > 0) { err = fat_alloc_clusters(inode, &cluster, 1); if (err) { fat_msg(sb, KERN_ERR, "fat_fallocate():fat_alloc_clusters() error"); goto error; } err = fat_chain_add(inode, cluster, 1); if (err) { fat_free_clusters(inode, cluster); goto error; } } err = fat_get_cluster(inode, FAT_ENT_EOF, &fclus, &dclus); if (err < 0) { fat_msg(sb, KERN_ERR, "fat_fallocate():fat_get_cluster() error"); goto error; } MSDOS_I(inode)->mmu_private = (fclus + 1) << sbi->cluster_bits; } else {
int msdos_lookup(struct inode *dir,const char *name,int len, struct inode **result) { int ino,res; struct msdos_dir_entry *de; struct buffer_head *bh; struct inode *next; *result = NULL; if (!dir) return -ENOENT; if (!S_ISDIR(dir->i_mode)) { iput(dir); return -ENOENT; } if (len == 1 && get_fs_byte(name) == '.') { *result = dir; return 0; } if (len == 2 && get_fs_byte(name) == '.' && get_fs_byte(name+1) == '.') { ino = msdos_parent_ino(dir,0); iput(dir); if (ino < 0) return ino; if (!(*result = iget(dir->i_dev,ino))) return -EACCES; return 0; } if ((res = msdos_find(dir,name,len,&bh,&de,&ino)) < 0) { iput(dir); return res; } if (bh) brelse(bh); /* printk("lookup: ino=%d\r\n",ino); */ if (!(*result = iget(dir->i_dev,ino))) { iput(dir); return -EACCES; } if (MSDOS_I(*result)->i_busy) { /* mkdir in progress */ iput(*result); iput(dir); return -ENOENT; } while (MSDOS_I(*result)->i_old) { next = MSDOS_I(*result)->i_old; iput(*result); if (!(*result = iget(next->i_dev,next->i_ino))) panic("msdos_lookup: Can't happen"); } iput(dir); return 0; }
int msdos_unlink(struct inode *dir,const char *name,int len) { int res,ino; struct buffer_head *bh; struct msdos_dir_entry *de; struct inode *inode; bh = NULL; inode = NULL; if ((res = msdos_find(dir,name,len,&bh,&de,&ino)) < 0) goto unlink_done; if (!(inode = iget(dir->i_dev,ino))) { res = -ENOENT; goto unlink_done; } if (!S_ISREG(inode->i_mode)) { res = -EPERM; goto unlink_done; } inode->i_nlink = 0; MSDOS_I(inode)->i_busy = 1; inode->i_dirt = 1; de->name[0] = DELETED_FLAG; bh->b_dirt = 1; unlink_done: brelse(bh); iput(inode); iput(dir); return res; }
ssize_t umsdos_file_write_kmem_real (struct file * filp, const char *buf, size_t count) { mm_segment_t old_fs = get_fs (); ssize_t ret; /* note: i_binary=2 is for CVF-FAT. We put it here, instead of * umsdos_file_write_kmem, since it is also wise not to compress * symlinks (in the unlikely event that they are > 512 bytes and * can be compressed. * FIXME: should we set it when reading symlinks too? */ MSDOS_I (filp->f_dentry->d_inode)->i_binary = 2; set_fs (KERNEL_DS); ret = fat_file_write (filp, buf, count, &filp->f_pos); set_fs (old_fs); if (ret < 0) { printk(KERN_WARNING "umsdos_file_write: ret=%d\n", ret); goto out; } #ifdef UMSDOS_PARANOIA if (ret != count) printk(KERN_WARNING "umsdos_file_write: count=%u, ret=%u\n", count, ret); #endif out: return ret; }
void msdos_truncate(struct inode *inode) { int cluster; cluster = SECTOR_SIZE*MSDOS_SB(inode->i_sb)->cluster_size; (void) fat_free(inode,(inode->i_size+(cluster-1))/cluster); MSDOS_I(inode)->i_attrs |= ATTR_ARCH; inode->i_dirt = 1; }
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); }
// root the info for root directory from disk and store it in inode // Return Value: -1 : error int fat_read_root(struct inode *inode) { struct super_block *sb = inode->i_sb; struct fat_sb_info *sbi = MSDOS_SB(sb); int error = 0; printk (KERN_INFO "myfat: fat_read_root\n"); MSDOS_I(inode)->i_pos = 0; inode->i_uid = sbi->options.fs_uid; inode->i_gid = sbi->options.fs_gid; inode->i_version++; inode->i_generation = 0; 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) { MSDOS_I(inode)->i_start = sbi->root_cluster; // the size of the file for Root Dir error = fat_calc_dir_size(inode); if (error < 0) return error; } else { MSDOS_I(inode)->i_start = 0; // the size of the file for Root Dir inode->i_size = sbi->dir_entries * sizeof(struct msdos_dir_entry); } inode->i_blocks = ((inode->i_size + (sbi->cluster_size - 1)) & ~((loff_t)sbi->cluster_size - 1)) >> 9; // why 9? // what if cluster size isn't 512? todo MSDOS_I(inode)->i_logstart = 0; MSDOS_I(inode)->mmu_private = inode->i_size; // rzq: I don't use this now // no idea what it is fat_save_attrs(inode, ATTR_DIR); // the is a directory 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; // count in . and .. return 0; }
ssize_t umsdos_file_read_kmem ( struct file *filp, char *buf, size_t count) { ssize_t ret; mm_segment_t old_fs = get_fs (); set_fs (KERNEL_DS); MSDOS_I (filp->f_dentry->d_inode)->i_binary = 2; ret = fat_file_read (filp, buf, count, &filp->f_pos); set_fs (old_fs); return ret; }
/* * Desc: Return the size of a directory file in bytes * */ static int fat_calc_dir_size(struct inode *inode) { struct fat_sb_info *sbi = MSDOS_SB(inode->i_sb); int ret, fclus, dclus; inode->i_size = 0; if (MSDOS_I(inode)->i_start == 0) return 0; ret = fat_get_cluster(inode, FAT_ENT_EOF, &fclus, &dclus); if (ret < 0) return ret; inode->i_size = (fclus + 1) << sbi->cluster_bits; return 0; }
int msdos_mkdir(struct inode *dir,const char *name,int len,int mode) { struct buffer_head *bh; struct msdos_dir_entry *de; struct inode *inode,*dot; char msdos_name[MSDOS_NAME]; int ino,res; if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->name_check,name,len, msdos_name)) < 0) { iput(dir); return res; } lock_creation(); if (msdos_scan(dir,msdos_name,&bh,&de,&ino) >= 0) { unlock_creation(); brelse(bh); iput(dir); return -EEXIST; } if ((res = msdos_create_entry(dir,msdos_name,1,&inode)) < 0) { unlock_creation(); iput(dir); return res; } dir->i_nlink++; inode->i_nlink = 2; /* no need to mark them dirty */ MSDOS_I(inode)->i_busy = 1; /* prevent lookups */ if ((res = msdos_add_cluster(inode)) < 0) goto mkdir_error; if ((res = msdos_create_entry(inode,MSDOS_DOT,1,&dot)) < 0) goto mkdir_error; dot->i_size = inode->i_size; /* doesn't grow in the 2nd create_entry */ MSDOS_I(dot)->i_start = MSDOS_I(inode)->i_start; dot->i_nlink = inode->i_nlink; dot->i_dirt = 1; iput(dot); if ((res = msdos_create_entry(inode,MSDOS_DOTDOT,1,&dot)) < 0) goto mkdir_error; unlock_creation(); dot->i_size = dir->i_size; MSDOS_I(dot)->i_start = MSDOS_I(dir)->i_start; dot->i_nlink = dir->i_nlink; dot->i_dirt = 1; MSDOS_I(inode)->i_busy = 0; iput(dot); iput(inode); iput(dir); return 0; mkdir_error: iput(inode); if (msdos_rmdir(dir,name,len) < 0) panic("rmdir in mkdir failed"); unlock_creation(); return res; }
int msdos_rmdir(struct inode *dir,const char *name,int len) { int res,ino,pos; struct buffer_head *bh,*dbh; struct msdos_dir_entry *de,*dde; struct inode *inode; bh = NULL; inode = NULL; res = -EINVAL; if (get_fs_byte(name) == '.' && (len == 1 || (len == 2 && get_fs_byte(name+1) == '.'))) goto rmdir_done; if ((res = msdos_find(dir,name,len,&bh,&de,&ino)) < 0) goto rmdir_done; res = -ENOENT; if (!(inode = iget(dir->i_dev,ino))) goto rmdir_done; res = -ENOTDIR; if (!S_ISDIR(inode->i_mode)) goto rmdir_done; res = -EBUSY; if (dir->i_dev != inode->i_dev || dir == inode) goto rmdir_done; if (inode->i_count > 1) goto rmdir_done; if (MSDOS_I(inode)->i_start) { /* may be zero in mkdir */ res = -ENOTEMPTY; pos = 0; dbh = NULL; while (msdos_get_entry(inode,&pos,&dbh,&dde) > -1) if (dde->name[0] && ((unsigned char *) dde->name)[0] != DELETED_FLAG && strncmp(dde->name,MSDOS_DOT, MSDOS_NAME) && strncmp(dde->name,MSDOS_DOTDOT, MSDOS_NAME)) goto rmdir_done; if (dbh) brelse(dbh); } inode->i_nlink = 0; dir->i_mtime = CURRENT_TIME; dir->i_nlink--; inode->i_dirt = dir->i_dirt = 1; de->name[0] = DELETED_FLAG; bh->b_dirt = 1; res = 0; rmdir_done: brelse(bh); iput(dir); iput(inode); return res; }
static int rename_same_dir(struct inode *old_dir,char *old_name, struct inode *new_dir,char *new_name,struct buffer_head *old_bh, struct msdos_dir_entry *old_de,int old_ino) { struct buffer_head *new_bh; struct msdos_dir_entry *new_de; struct inode *new_inode,*old_inode; int new_ino; int exists; if (!strncmp(old_name,new_name,MSDOS_NAME)) return 0; exists = msdos_scan(new_dir,new_name,&new_bh,&new_de,&new_ino) >= 0; if (*(unsigned char *) old_de->name == DELETED_FLAG) { if (exists) brelse(new_bh); return -ENOENT; } if (exists) { if (!(new_inode = iget(new_dir->i_dev,new_ino))) { brelse(new_bh); return -EIO; } if (S_ISDIR(new_inode->i_mode)) { iput(new_inode); brelse(new_bh); return -EPERM; } new_inode->i_nlink = 0; MSDOS_I(new_inode)->i_busy = 1; new_inode->i_dirt = 1; new_de->name[0] = DELETED_FLAG; new_bh->b_dirt = 1; iput(new_inode); brelse(new_bh); } memcpy(old_de->name,new_name,MSDOS_NAME); old_bh->b_dirt = 1; if (MSDOS_SB(old_dir->i_sb)->conversion == 'a') /* update binary info */ if (old_inode = iget(old_dir->i_dev,old_ino)) { msdos_read_inode(old_inode); iput(old_inode); } return 0; }
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, struct buffer_head *old_bh, struct msdos_dir_entry *old_de, loff_t old_i_pos, int is_hid) { struct buffer_head *new_bh=NULL,*dotdot_bh=NULL; struct msdos_dir_entry *new_de,*dotdot_de; struct inode *old_inode,*new_inode; loff_t new_i_pos, dotdot_i_pos; int error; int is_dir; old_inode = old_dentry->d_inode; new_inode = new_dentry->d_inode; is_dir = S_ISDIR(old_inode->i_mode); if (fat_scan(new_dir, new_name, &new_bh, &new_de, &new_i_pos) >= 0 && !new_inode) goto degenerate_case; if (is_dir) { if (new_inode) { error = fat_dir_empty(new_inode); if (error) goto out; } if (fat_scan(old_inode, MSDOS_DOTDOT, &dotdot_bh, &dotdot_de, &dotdot_i_pos) < 0) { error = -EIO; goto out; } } if (!new_bh) { error = msdos_add_entry(new_dir, new_name, &new_bh, &new_de, &new_i_pos, is_dir, is_hid); if (error) goto out; } new_dir->i_version++; /* There we go */ if (new_inode) fat_detach(new_inode); old_de->name[0] = DELETED_FLAG; mark_buffer_dirty(old_bh); 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; mark_inode_dirty(old_inode); old_dir->i_version++; old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; mark_inode_dirty(old_dir); if (new_inode) { new_inode->i_nlink--; new_inode->i_ctime = CURRENT_TIME; mark_inode_dirty(new_inode); } if (dotdot_bh) { dotdot_de->start = CT_LE_W(MSDOS_I(new_dir)->i_logstart); dotdot_de->starthi = CT_LE_W((MSDOS_I(new_dir)->i_logstart) >> 16); mark_buffer_dirty(dotdot_bh); old_dir->i_nlink--; mark_inode_dirty(old_dir); if (new_inode) { new_inode->i_nlink--; mark_inode_dirty(new_inode); } else { new_dir->i_nlink++; mark_inode_dirty(new_dir); } } error = 0; out: brelse(new_bh); brelse(dotdot_bh); return error; degenerate_case: error = -EINVAL; if (new_de!=old_de) goto out; if (is_hid) MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN; else MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN; mark_inode_dirty(old_inode); old_dir->i_version++; old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; mark_inode_dirty(old_dir); return 0; }
static void fat_destroy_inode(struct inode *inode) { printk (KERN_INFO "myfat: fat_destroy_inode\n"); kmem_cache_free(fat_inode_cachep, MSDOS_I(inode)); }
/* Write to a file either from user space */ int msdos_file_write( struct inode *inode, struct file *filp, const char *buf, int count) { struct super_block *sb = inode->i_sb; int sector,offset,size,left,written; int error,carry; char *start,*to,ch; struct buffer_head *bh; int binary_mode = MSDOS_I(inode)->i_binary; if (!inode) { printk("msdos_file_write: inode = NULL\n"); return -EINVAL; } /* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */ if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) { printk("msdos_file_write: mode = %07o\n",inode->i_mode); return -EINVAL; } /* * ok, append may not work when many processes are writing at the same time * but so what. That way leads to madness anyway. */ if (filp->f_flags & O_APPEND) filp->f_pos = inode->i_size; if (count <= 0) return 0; error = carry = 0; for (start = (char *)buf; count || carry; count -= size) { while (!(sector = msdos_smap(inode,filp->f_pos >> SECTOR_BITS))) if ((error = msdos_add_cluster(inode)) < 0) break; if (error) { msdos_truncate(inode); break; } offset = filp->f_pos & (SECTOR_SIZE-1); size = MIN(SECTOR_SIZE-offset,MAX(carry,count)); if (binary_mode && offset == 0 && (size == SECTOR_SIZE || filp->f_pos + size >= inode->i_size)){ /* No need to read the block first since we will */ /* completely overwrite it */ /* or at least write past the end of file */ if (!(bh = getblk(inode->i_dev,sector,SECTOR_SIZE))){ error = -EIO; break; } }else if (!(bh = bread(inode->i_dev,sector,SECTOR_SIZE))) { error = -EIO; break; } if (binary_mode) { memcpy_fromfs(bh->b_data+offset,buf,written = size); buf += size; } else { written = left = SECTOR_SIZE-offset; to = (char *) bh->b_data+(filp->f_pos & (SECTOR_SIZE-1)); if (carry) { *to++ = '\n'; left--; carry = 0; } for (size = 0; size < count && left; size++) { if ((ch = get_fs_byte(buf++)) == '\n') { *to++ = '\r'; left--; } if (!left) carry = 1; else { *to++ = ch; left--; } } written -= left; } filp->f_pos += written; if (filp->f_pos > inode->i_size) { inode->i_size = filp->f_pos; inode->i_dirt = 1; } msdos_set_uptodate(sb,bh,1); mark_buffer_dirty(bh, 0); brelse(bh); } if (start == buf) return error; inode->i_mtime = inode->i_ctime = FS_CURRENT_TIME; MSDOS_I(inode)->i_attrs |= ATTR_ARCH; inode->i_dirt = 1; return buf-start; }
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 */ } }
int isNodeContinuous(struct inode *inode) { struct fat_entry fatent; int dclus = 0; int nr = 0; struct msdos_inode_info * msinode= NULL; struct super_block *sb = NULL; struct msdos_sb_info *sbi = NULL; struct msdos_dir_entry *uninitialized_var(de); /*if (NULL != file && NULL != file->f_mapping) { inode = file->f_mapping->host; } else { printk(KERN_INFO "file or file->f_mapping is null, file:%x\n", (unsigned int)file); return 0; }*/ if (NULL != inode) { msinode= MSDOS_I(inode); sb = inode->i_sb; } else { printk(KERN_INFO "inode is null\n"); return 0; } //printk(KERN_INFO "3, inode:%x\n", (unsigned int)inode); if (NULL == msinode) { printk(KERN_INFO "msinode is null\n"); return 0; } if (NULL != sb) { sbi = MSDOS_SB(sb); } if (NULL == sbi) { printk(KERN_INFO "sbi is null\n"); return 0; } if (msinode->i_start < FAT_START_ENT || msinode->i_start > sbi->max_cluster) { printk(KERN_INFO "!!!start cluster is not correct\n"); return 1; } lock_fat2(sbi); dclus = msinode->i_start; fatent_init(&fatent); while (dclus < FAT_ENT_EOF) { //printk(KERN_INFO "%d ", dclus); nr = fat_ent_read(inode, &fatent, dclus); if (FAT_ENT_EOF != nr && nr != (dclus + 1)) { fatent_brelse(&fatent); unlock_fat2(sbi); printk(KERN_INFO "file is not contiguous\n"); return 0; } dclus = nr; } fatent_brelse(&fatent); unlock_fat2(sbi); return 1; }
static int rename_diff_dir(struct inode *old_dir,char *old_name, struct inode *new_dir,char *new_name,struct buffer_head *old_bh, struct msdos_dir_entry *old_de,int old_ino) { struct buffer_head *new_bh,*free_bh,*dotdot_bh; struct msdos_dir_entry *new_de,*free_de,*dotdot_de; struct inode *old_inode,*new_inode,*free_inode,*dotdot_inode,*walk; int new_ino,free_ino,dotdot_ino; int error,exists,ino; if (old_dir->i_dev != new_dir->i_dev) return -EINVAL; if (old_ino == new_dir->i_ino) return -EINVAL; if (!(walk = iget(new_dir->i_dev,new_dir->i_ino))) return -EIO; while (walk->i_ino != MSDOS_ROOT_INO) { ino = msdos_parent_ino(walk,1); iput(walk); if (ino < 0) return ino; if (ino == old_ino) return -EINVAL; if (!(walk = iget(new_dir->i_dev,ino))) return -EIO; } iput(walk); if ((error = msdos_scan(new_dir,NULL,&free_bh,&free_de,&free_ino)) < 0) return error; exists = msdos_scan(new_dir,new_name,&new_bh,&new_de,&new_ino) >= 0; if (!(old_inode = iget(old_dir->i_dev,old_ino))) { brelse(free_bh); if (exists) brelse(new_bh); return -EIO; } if (*(unsigned char *) old_de->name == DELETED_FLAG) { iput(old_inode); brelse(free_bh); if (exists) brelse(new_bh); return -ENOENT; } new_inode = NULL; /* to make GCC happy */ if (exists) { if (!(new_inode = iget(new_dir->i_dev,new_ino))) { iput(old_inode); brelse(new_bh); return -EIO; } if (S_ISDIR(new_inode->i_mode)) { iput(new_inode); iput(old_inode); brelse(new_bh); return -EPERM; } new_inode->i_nlink = 0; MSDOS_I(new_inode)->i_busy = 1; new_inode->i_dirt = 1; new_de->name[0] = DELETED_FLAG; new_bh->b_dirt = 1; } memcpy(free_de,old_de,sizeof(struct msdos_dir_entry)); memcpy(free_de->name,new_name,MSDOS_NAME); if (!(free_inode = iget(new_dir->i_dev,free_ino))) { free_de->name[0] = DELETED_FLAG; /* Don't mark free_bh as dirty. Both states are supposed to be equivalent. */ brelse(free_bh); if (exists) { iput(new_inode); brelse(new_bh); } return -EIO; } msdos_read_inode(free_inode); MSDOS_I(old_inode)->i_busy = 1; cache_inval_inode(old_inode); old_inode->i_dirt = 1; old_de->name[0] = DELETED_FLAG; old_bh->b_dirt = 1; free_bh->b_dirt = 1; if (!exists) iput(free_inode); else { MSDOS_I(new_inode)->i_depend = free_inode; MSDOS_I(free_inode)->i_old = new_inode; /* free_inode is put when putting new_inode */ iput(new_inode); brelse(new_bh); } if (S_ISDIR(old_inode->i_mode)) { if ((error = msdos_scan(old_inode,MSDOS_DOTDOT,&dotdot_bh, &dotdot_de,&dotdot_ino)) < 0) goto rename_done; if (!(dotdot_inode = iget(old_inode->i_dev,dotdot_ino))) { brelse(dotdot_bh); error = -EIO; goto rename_done; } dotdot_de->start = MSDOS_I(dotdot_inode)->i_start = MSDOS_I(new_dir)->i_start; dotdot_inode->i_dirt = 1; dotdot_bh->b_dirt = 1; iput(dotdot_inode); brelse(dotdot_bh); old_dir->i_nlink--; new_dir->i_nlink++; /* no need to mark them dirty */ } error = 0; rename_done: brelse(free_bh); iput(old_inode); return error; }
/* 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); }
/* Read a file into user space */ int msdos_file_read( struct inode *inode, struct file *filp, char *buf, int count) { struct super_block *sb = inode->i_sb; char *start = buf; char *end = buf + count; int i; int left_in_file; struct msdos_pre pre; if (!inode) { printk("msdos_file_read: inode = NULL\n"); return -EINVAL; } /* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */ if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) { printk("msdos_file_read: mode = %07o\n",inode->i_mode); return -EINVAL; } if (filp->f_pos >= inode->i_size || count <= 0) return 0; /* Tell the buffer cache which block we expect to read in advance Since we are limited with the stack, we preread only MSDOS_PREFETCH because we have to keep the result into the local arrays pre.bhlist and bhreq. Each time we process one block in bhlist, we replace it by a new prefetch block if needed. */ PRINTK (("#### ino %ld pos %ld size %ld count %d\n",inode->i_ino,filp->f_pos,inode->i_size,count)); { /* We must prefetch complete block, so we must take in account the offset in the first block. */ int count_max = (filp->f_pos & (SECTOR_SIZE-1)) + count; int to_reada; /* How many block to read all at once */ pre.file_sector = filp->f_pos >> SECTOR_BITS; to_reada = count_max / SECTOR_SIZE; if (count_max & (SECTOR_SIZE-1)) to_reada++; if (filp->f_reada || !MSDOS_I(inode)->i_binary){ /* Doing a read ahead on ascii file make sure we always */ /* pre read enough, since we don't know how many blocks */ /* we really need */ int ahead = read_ahead[MAJOR(inode->i_dev)]; PRINTK (("to_reada %d ahead %d\n",to_reada,ahead)); if (ahead == 0) ahead = 8; to_reada += ahead; } if (to_reada > MSDOS_PREFETCH) to_reada = MSDOS_PREFETCH; pre.nblist = 0; msdos_prefetch (inode,&pre,to_reada); } pre.nolist = 0; PRINTK (("count %d ahead %d nblist %d\n",count,read_ahead[MAJOR(inode->i_dev)],pre.nblist)); while ((left_in_file = inode->i_size - filp->f_pos) > 0 && buf < end){ struct buffer_head *bh = pre.bhlist[pre.nolist]; char *data; int size,offset; if (bh == NULL) break; pre.bhlist[pre.nolist] = NULL; pre.nolist++; if (pre.nolist == MSDOS_PREFETCH/2){ memcpy (pre.bhlist,pre.bhlist+MSDOS_PREFETCH/2 ,(MSDOS_PREFETCH/2)*sizeof(pre.bhlist[0])); pre.nblist -= MSDOS_PREFETCH/2; msdos_prefetch (inode,&pre,MSDOS_PREFETCH/2); pre.nolist = 0; } PRINTK (("file_read pos %ld nblist %d %d %d\n",filp->f_pos,pre.nblist,pre.fetched,count)); wait_on_buffer(bh); if (!msdos_is_uptodate(sb,bh)){ /* read error ? */ brelse (bh); break; } offset = filp->f_pos & (SECTOR_SIZE-1); data = bh->b_data + offset; size = MIN(SECTOR_SIZE-offset,left_in_file); if (MSDOS_I(inode)->i_binary) { size = MIN(size,end-buf); memcpy_tofs(buf,data,size); buf += size; filp->f_pos += size; }else{ for (; size && buf < end; size--) { char ch = *data++; filp->f_pos++; if (ch == 26){ filp->f_pos = inode->i_size; break; }else if (ch != '\r'){ put_fs_byte(ch,buf++); } } } brelse(bh); } PRINTK (("--- %d -> %d\n",count,(int)(buf-start))); for (i=0; i<pre.nblist; i++) brelse (pre.bhlist[i]); if (start == buf) return -EIO; if (!IS_RDONLY(inode)) inode->i_atime = FS_CURRENT_TIME; filp->f_reada = 1; /* Will be reset if a lseek is done */ return buf-start; }
static int do_msdos_rename(struct inode *old_dir, char *old_name, struct dentry *old_dentry, struct inode *new_dir,char *new_name, struct dentry *new_dentry, struct buffer_head *old_bh, struct msdos_dir_entry *old_de, loff_t old_ino, int is_hid) { struct super_block *sb = old_dir->i_sb; struct buffer_head *new_bh=NULL,*dotdot_bh=NULL; struct msdos_dir_entry *new_de,*dotdot_de; struct inode *old_inode,*new_inode; loff_t new_ino,dotdot_ino; int error; int is_dir; old_inode = old_dentry->d_inode; new_inode = new_dentry->d_inode; is_dir = S_ISDIR(old_inode->i_mode); if (fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino)>=0 &&!new_inode) goto degenerate_case; if (is_dir) { if (new_inode) { error = fat_dir_empty(new_inode); if (error) goto out; } error = fat_scan(old_inode, MSDOS_DOTDOT, &dotdot_bh, &dotdot_de, &dotdot_ino); if (error < 0) { printk(KERN_WARNING "MSDOS: %s/%s, get dotdot failed, ret=%d\n", old_dentry->d_parent->d_name.name, old_dentry->d_name.name, error); goto out; } } if (!new_bh) { error = msdos_add_entry(new_dir, new_name, &new_bh, &new_de, &new_ino, is_dir, is_hid); if (error) goto out; } new_dir->i_version = ++event; /* There we go */ if (new_inode) fat_detach(new_inode); old_de->name[0] = DELETED_FLAG; fat_mark_buffer_dirty(sb, old_bh); fat_detach(old_inode); fat_attach(old_inode, new_ino); if (is_hid) MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN; else MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN; mark_inode_dirty(old_inode); old_dir->i_version = ++event; old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; mark_inode_dirty(old_dir); if (new_inode) { new_inode->i_nlink--; new_inode->i_ctime = CURRENT_TIME; mark_inode_dirty(new_inode); } if (dotdot_bh) { dotdot_de->start = CT_LE_W(MSDOS_I(new_dir)->i_logstart); dotdot_de->starthi = CT_LE_W((MSDOS_I(new_dir)->i_logstart) >> 16); fat_mark_buffer_dirty(sb, dotdot_bh); old_dir->i_nlink--; mark_inode_dirty(old_dir); if (new_inode) { new_inode->i_nlink--; mark_inode_dirty(new_inode); } else { new_dir->i_nlink++; mark_inode_dirty(new_dir); } } error = 0; out: fat_brelse(sb, new_bh); fat_brelse(sb, dotdot_bh); return error; degenerate_case: error = -EINVAL; if (new_de!=old_de) goto out; if (is_hid) MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN; else MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN; mark_inode_dirty(old_inode); old_dir->i_version = ++event; old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; mark_inode_dirty(old_dir); return 0; }
static int fat_ioctl_set_attributes(struct file *file, u32 __user *user_attr) { struct inode *inode = file_inode(file); struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); int is_dir = S_ISDIR(inode->i_mode); u32 attr, oldattr; struct iattr ia; int err; err = get_user(attr, user_attr); if (err) goto out; err = mnt_want_write_file(file); if (err) goto out; inode_lock(inode); /* * 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_make_attrs(inode); /* Equivalent to a chmod() */ ia.ia_valid = ATTR_MODE | ATTR_CTIME; ia.ia_ctime = current_time(inode); 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 */ if (inode->i_ino == MSDOS_ROOT_INO && attr != ATTR_DIR) { err = -EINVAL; goto out_unlock_inode; } if (sbi->options.sys_immutable && ((attr | oldattr) & ATTR_SYS) && !capable(CAP_LINUX_IMMUTABLE)) { err = -EPERM; goto out_unlock_inode; } /* * The security check is questionable... We single * out the RO attribute for checking by the security * module, just because it maps to a file mode. */ err = security_inode_setattr(file->f_path.dentry, &ia); if (err) goto out_unlock_inode; /* This MUST be done before doing anything irreversible... */ err = fat_setattr(file->f_path.dentry, &ia); if (err) goto out_unlock_inode; fsnotify_change(file->f_path.dentry, ia.ia_valid); if (sbi->options.sys_immutable) { if (attr & ATTR_SYS) inode->i_flags |= S_IMMUTABLE; else inode->i_flags &= ~S_IMMUTABLE; } fat_save_attrs(inode, attr); mark_inode_dirty(inode); out_unlock_inode: inode_unlock(inode); mnt_drop_write_file(file); out: return err; }
static int do_vxext_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 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)) { 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 = vxext_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) { fat_set_start(dotdot_de, MSDOS_I(new_dir)->i_logstart); mark_buffer_dirty_inode(dotdot_bh, old_inode); 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) { fat_set_start(dotdot_de, MSDOS_I(old_dir)->i_logstart); mark_buffer_dirty_inode(dotdot_bh, old_inode); 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_error(new_dir->i_sb, "%s: Filesystem corrupted (i_pos %lld)", __func__, sinfo.i_pos); } goto out; }
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) { 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_inode(dotdot_bh, old_inode); 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); 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: 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_inode(dotdot_bh, old_inode); 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 { int err2 = fat_remove_entries(new_dir, &sinfo); if (corrupt) corrupt |= err2; sinfo.bh = NULL; } if (corrupt < 0) { fat_fs_error(new_dir->i_sb, "%s: Filesystem corrupted (i_pos %lld)", __func__, sinfo.i_pos); } goto out; }