Skip to content

Commit 98d9170

Browse files
namjaejeonAl Viro
authored andcommitted
exfat: add file operations
This adds the implementation of file operations for exfat. Signed-off-by: Namjae Jeon <[email protected]> Signed-off-by: Sungjong Seo <[email protected]> Reviewed-by: Pali Rohár <[email protected]> Reviewed-by: Christoph Hellwig <[email protected]> Reviewed-by: Arnd Bergmann <[email protected]> Signed-off-by: Al Viro <[email protected]>
1 parent ca06197 commit 98d9170

File tree

1 file changed

+360
-0
lines changed

1 file changed

+360
-0
lines changed

fs/exfat/file.c

Lines changed: 360 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,360 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
/*
3+
* Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
4+
*/
5+
6+
#include <linux/slab.h>
7+
#include <linux/cred.h>
8+
#include <linux/buffer_head.h>
9+
10+
#include "exfat_raw.h"
11+
#include "exfat_fs.h"
12+
13+
static int exfat_cont_expand(struct inode *inode, loff_t size)
14+
{
15+
struct address_space *mapping = inode->i_mapping;
16+
loff_t start = i_size_read(inode), count = size - i_size_read(inode);
17+
int err, err2;
18+
19+
err = generic_cont_expand_simple(inode, size);
20+
if (err)
21+
return err;
22+
23+
inode->i_ctime = inode->i_mtime = current_time(inode);
24+
mark_inode_dirty(inode);
25+
26+
if (!IS_SYNC(inode))
27+
return 0;
28+
29+
err = filemap_fdatawrite_range(mapping, start, start + count - 1);
30+
err2 = sync_mapping_buffers(mapping);
31+
if (!err)
32+
err = err2;
33+
err2 = write_inode_now(inode, 1);
34+
if (!err)
35+
err = err2;
36+
if (err)
37+
return err;
38+
39+
return filemap_fdatawait_range(mapping, start, start + count - 1);
40+
}
41+
42+
static bool exfat_allow_set_time(struct exfat_sb_info *sbi, struct inode *inode)
43+
{
44+
mode_t allow_utime = sbi->options.allow_utime;
45+
46+
if (!uid_eq(current_fsuid(), inode->i_uid)) {
47+
if (in_group_p(inode->i_gid))
48+
allow_utime >>= 3;
49+
if (allow_utime & MAY_WRITE)
50+
return true;
51+
}
52+
53+
/* use a default check */
54+
return false;
55+
}
56+
57+
static int exfat_sanitize_mode(const struct exfat_sb_info *sbi,
58+
struct inode *inode, umode_t *mode_ptr)
59+
{
60+
mode_t i_mode, mask, perm;
61+
62+
i_mode = inode->i_mode;
63+
64+
mask = (S_ISREG(i_mode) || S_ISLNK(i_mode)) ?
65+
sbi->options.fs_fmask : sbi->options.fs_dmask;
66+
perm = *mode_ptr & ~(S_IFMT | mask);
67+
68+
/* Of the r and x bits, all (subject to umask) must be present.*/
69+
if ((perm & 0555) != (i_mode & 0555))
70+
return -EPERM;
71+
72+
if (exfat_mode_can_hold_ro(inode)) {
73+
/*
74+
* Of the w bits, either all (subject to umask) or none must
75+
* be present.
76+
*/
77+
if ((perm & 0222) && ((perm & 0222) != (0222 & ~mask)))
78+
return -EPERM;
79+
} else {
80+
/*
81+
* If exfat_mode_can_hold_ro(inode) is false, can't change
82+
* w bits.
83+
*/
84+
if ((perm & 0222) != (0222 & ~mask))
85+
return -EPERM;
86+
}
87+
88+
*mode_ptr &= S_IFMT | perm;
89+
90+
return 0;
91+
}
92+
93+
/* resize the file length */
94+
int __exfat_truncate(struct inode *inode, loff_t new_size)
95+
{
96+
unsigned int num_clusters_new, num_clusters_phys;
97+
unsigned int last_clu = EXFAT_FREE_CLUSTER;
98+
struct exfat_chain clu;
99+
struct exfat_dentry *ep, *ep2;
100+
struct super_block *sb = inode->i_sb;
101+
struct exfat_sb_info *sbi = EXFAT_SB(sb);
102+
struct exfat_inode_info *ei = EXFAT_I(inode);
103+
struct exfat_entry_set_cache *es = NULL;
104+
int evict = (ei->dir.dir == DIR_DELETED) ? 1 : 0;
105+
106+
/* check if the given file ID is opened */
107+
if (ei->type != TYPE_FILE && ei->type != TYPE_DIR)
108+
return -EPERM;
109+
110+
exfat_set_vol_flags(sb, VOL_DIRTY);
111+
112+
num_clusters_new = EXFAT_B_TO_CLU_ROUND_UP(i_size_read(inode), sbi);
113+
num_clusters_phys =
114+
EXFAT_B_TO_CLU_ROUND_UP(EXFAT_I(inode)->i_size_ondisk, sbi);
115+
116+
exfat_chain_set(&clu, ei->start_clu, num_clusters_phys, ei->flags);
117+
118+
if (new_size > 0) {
119+
/*
120+
* Truncate FAT chain num_clusters after the first cluster
121+
* num_clusters = min(new, phys);
122+
*/
123+
unsigned int num_clusters =
124+
min(num_clusters_new, num_clusters_phys);
125+
126+
/*
127+
* Follow FAT chain
128+
* (defensive coding - works fine even with corrupted FAT table
129+
*/
130+
if (clu.flags == ALLOC_NO_FAT_CHAIN) {
131+
clu.dir += num_clusters;
132+
clu.size -= num_clusters;
133+
} else {
134+
while (num_clusters > 0) {
135+
last_clu = clu.dir;
136+
if (exfat_get_next_cluster(sb, &(clu.dir)))
137+
return -EIO;
138+
139+
num_clusters--;
140+
clu.size--;
141+
}
142+
}
143+
} else {
144+
ei->flags = ALLOC_NO_FAT_CHAIN;
145+
ei->start_clu = EXFAT_EOF_CLUSTER;
146+
}
147+
148+
i_size_write(inode, new_size);
149+
150+
if (ei->type == TYPE_FILE)
151+
ei->attr |= ATTR_ARCHIVE;
152+
153+
/* update the directory entry */
154+
if (!evict) {
155+
struct timespec64 ts;
156+
157+
es = exfat_get_dentry_set(sb, &(ei->dir), ei->entry,
158+
ES_ALL_ENTRIES, &ep);
159+
if (!es)
160+
return -EIO;
161+
ep2 = ep + 1;
162+
163+
ts = current_time(inode);
164+
exfat_set_entry_time(sbi, &ts,
165+
&ep->dentry.file.modify_tz,
166+
&ep->dentry.file.modify_time,
167+
&ep->dentry.file.modify_date,
168+
&ep->dentry.file.modify_time_ms);
169+
ep->dentry.file.attr = cpu_to_le16(ei->attr);
170+
171+
/* File size should be zero if there is no cluster allocated */
172+
if (ei->start_clu == EXFAT_EOF_CLUSTER) {
173+
ep->dentry.stream.valid_size = 0;
174+
ep->dentry.stream.size = 0;
175+
} else {
176+
ep->dentry.stream.valid_size = cpu_to_le64(new_size);
177+
ep->dentry.stream.size = ep->dentry.stream.valid_size;
178+
}
179+
180+
if (new_size == 0) {
181+
/* Any directory can not be truncated to zero */
182+
WARN_ON(ei->type != TYPE_FILE);
183+
184+
ep2->dentry.stream.flags = ALLOC_FAT_CHAIN;
185+
ep2->dentry.stream.start_clu = EXFAT_FREE_CLUSTER;
186+
}
187+
188+
if (exfat_update_dir_chksum_with_entry_set(sb, es,
189+
inode_needs_sync(inode)))
190+
return -EIO;
191+
kfree(es);
192+
}
193+
194+
/* cut off from the FAT chain */
195+
if (ei->flags == ALLOC_FAT_CHAIN && last_clu != EXFAT_FREE_CLUSTER &&
196+
last_clu != EXFAT_EOF_CLUSTER) {
197+
if (exfat_ent_set(sb, last_clu, EXFAT_EOF_CLUSTER))
198+
return -EIO;
199+
}
200+
201+
/* invalidate cache and free the clusters */
202+
/* clear exfat cache */
203+
exfat_cache_inval_inode(inode);
204+
205+
/* hint information */
206+
ei->hint_bmap.off = EXFAT_EOF_CLUSTER;
207+
ei->hint_bmap.clu = EXFAT_EOF_CLUSTER;
208+
if (ei->rwoffset > new_size)
209+
ei->rwoffset = new_size;
210+
211+
/* hint_stat will be used if this is directory. */
212+
ei->hint_stat.eidx = 0;
213+
ei->hint_stat.clu = ei->start_clu;
214+
ei->hint_femp.eidx = EXFAT_HINT_NONE;
215+
216+
/* free the clusters */
217+
if (exfat_free_cluster(inode, &clu))
218+
return -EIO;
219+
220+
exfat_set_vol_flags(sb, VOL_CLEAN);
221+
222+
return 0;
223+
}
224+
225+
void exfat_truncate(struct inode *inode, loff_t size)
226+
{
227+
struct super_block *sb = inode->i_sb;
228+
struct exfat_sb_info *sbi = EXFAT_SB(sb);
229+
unsigned int blocksize = 1 << inode->i_blkbits;
230+
loff_t aligned_size;
231+
int err;
232+
233+
mutex_lock(&sbi->s_lock);
234+
if (EXFAT_I(inode)->start_clu == 0) {
235+
/*
236+
* Empty start_clu != ~0 (not allocated)
237+
*/
238+
exfat_fs_error(sb, "tried to truncate zeroed cluster.");
239+
goto write_size;
240+
}
241+
242+
err = __exfat_truncate(inode, i_size_read(inode));
243+
if (err)
244+
goto write_size;
245+
246+
inode->i_ctime = inode->i_mtime = current_time(inode);
247+
if (IS_DIRSYNC(inode))
248+
exfat_sync_inode(inode);
249+
else
250+
mark_inode_dirty(inode);
251+
252+
inode->i_blocks = ((i_size_read(inode) + (sbi->cluster_size - 1)) &
253+
~(sbi->cluster_size - 1)) >> inode->i_blkbits;
254+
write_size:
255+
aligned_size = i_size_read(inode);
256+
if (aligned_size & (blocksize - 1)) {
257+
aligned_size |= (blocksize - 1);
258+
aligned_size++;
259+
}
260+
261+
if (EXFAT_I(inode)->i_size_ondisk > i_size_read(inode))
262+
EXFAT_I(inode)->i_size_ondisk = aligned_size;
263+
264+
if (EXFAT_I(inode)->i_size_aligned > i_size_read(inode))
265+
EXFAT_I(inode)->i_size_aligned = aligned_size;
266+
mutex_unlock(&sbi->s_lock);
267+
}
268+
269+
int exfat_getattr(const struct path *path, struct kstat *stat,
270+
unsigned int request_mask, unsigned int query_flags)
271+
{
272+
struct inode *inode = d_backing_inode(path->dentry);
273+
struct exfat_inode_info *ei = EXFAT_I(inode);
274+
275+
generic_fillattr(inode, stat);
276+
stat->result_mask |= STATX_BTIME;
277+
stat->btime.tv_sec = ei->i_crtime.tv_sec;
278+
stat->btime.tv_nsec = ei->i_crtime.tv_nsec;
279+
stat->blksize = EXFAT_SB(inode->i_sb)->cluster_size;
280+
return 0;
281+
}
282+
283+
int exfat_setattr(struct dentry *dentry, struct iattr *attr)
284+
{
285+
struct exfat_sb_info *sbi = EXFAT_SB(dentry->d_sb);
286+
struct inode *inode = dentry->d_inode;
287+
unsigned int ia_valid;
288+
int error;
289+
290+
if ((attr->ia_valid & ATTR_SIZE) &&
291+
attr->ia_size > i_size_read(inode)) {
292+
error = exfat_cont_expand(inode, attr->ia_size);
293+
if (error || attr->ia_valid == ATTR_SIZE)
294+
return error;
295+
attr->ia_valid &= ~ATTR_SIZE;
296+
}
297+
298+
/* Check for setting the inode time. */
299+
ia_valid = attr->ia_valid;
300+
if ((ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)) &&
301+
exfat_allow_set_time(sbi, inode)) {
302+
attr->ia_valid &= ~(ATTR_MTIME_SET | ATTR_ATIME_SET |
303+
ATTR_TIMES_SET);
304+
}
305+
306+
error = setattr_prepare(dentry, attr);
307+
attr->ia_valid = ia_valid;
308+
if (error)
309+
goto out;
310+
311+
if (((attr->ia_valid & ATTR_UID) &&
312+
!uid_eq(attr->ia_uid, sbi->options.fs_uid)) ||
313+
((attr->ia_valid & ATTR_GID) &&
314+
!gid_eq(attr->ia_gid, sbi->options.fs_gid)) ||
315+
((attr->ia_valid & ATTR_MODE) &&
316+
(attr->ia_mode & ~(S_IFREG | S_IFLNK | S_IFDIR | 0777)))) {
317+
error = -EPERM;
318+
goto out;
319+
}
320+
321+
/*
322+
* We don't return -EPERM here. Yes, strange, but this is too
323+
* old behavior.
324+
*/
325+
if (attr->ia_valid & ATTR_MODE) {
326+
if (exfat_sanitize_mode(sbi, inode, &attr->ia_mode) < 0)
327+
attr->ia_valid &= ~ATTR_MODE;
328+
}
329+
330+
if (attr->ia_valid & ATTR_SIZE) {
331+
error = exfat_block_truncate_page(inode, attr->ia_size);
332+
if (error)
333+
goto out;
334+
335+
down_write(&EXFAT_I(inode)->truncate_lock);
336+
truncate_setsize(inode, attr->ia_size);
337+
exfat_truncate(inode, attr->ia_size);
338+
up_write(&EXFAT_I(inode)->truncate_lock);
339+
}
340+
341+
setattr_copy(inode, attr);
342+
mark_inode_dirty(inode);
343+
344+
out:
345+
return error;
346+
}
347+
348+
const struct file_operations exfat_file_operations = {
349+
.llseek = generic_file_llseek,
350+
.read_iter = generic_file_read_iter,
351+
.write_iter = generic_file_write_iter,
352+
.mmap = generic_file_mmap,
353+
.fsync = generic_file_fsync,
354+
.splice_read = generic_file_splice_read,
355+
};
356+
357+
const struct inode_operations exfat_file_inode_operations = {
358+
.setattr = exfat_setattr,
359+
.getattr = exfat_getattr,
360+
};

0 commit comments

Comments
 (0)