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