Ejemplo n.º 1
0
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 {
Ejemplo n.º 2
0
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;
}
Ejemplo n.º 3
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;
}
Ejemplo n.º 4
0
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;
}
Ejemplo n.º 5
0
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;
}
Ejemplo n.º 6
0
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);
}
Ejemplo n.º 7
0
// 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;
}
Ejemplo n.º 8
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;
}
Ejemplo n.º 9
0
/*
* 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;
}
Ejemplo n.º 10
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;
}
Ejemplo n.º 11
0
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;
}
Ejemplo n.º 12
0
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;
}
Ejemplo n.º 13
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;
}
Ejemplo n.º 14
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));
}
Ejemplo n.º 15
0
/*
	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;
}
Ejemplo n.º 16
0
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;
}
Ejemplo n.º 18
0
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;
}
Ejemplo n.º 19
0
/* 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);
}
Ejemplo n.º 20
0
/*
	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;
}
Ejemplo n.º 22
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;
}
Ejemplo n.º 23
0
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;
}