file.c 15.6 KB
Newer Older
Jaegeuk Kim's avatar
Jaegeuk Kim committed
1
/*
Jaegeuk Kim's avatar
Jaegeuk Kim committed
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 * fs/f2fs/file.c
 *
 * Copyright (c) 2012 Samsung Electronics Co., Ltd.
 *             http://www.samsung.com/
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */
#include <linux/fs.h>
#include <linux/f2fs_fs.h>
#include <linux/stat.h>
#include <linux/buffer_head.h>
#include <linux/writeback.h>
16
#include <linux/blkdev.h>
Jaegeuk Kim's avatar
Jaegeuk Kim committed
17
18
#include <linux/falloc.h>
#include <linux/types.h>
19
#include <linux/compat.h>
Jaegeuk Kim's avatar
Jaegeuk Kim committed
20
21
22
23
24
25
26
27
#include <linux/uaccess.h>
#include <linux/mount.h>

#include "f2fs.h"
#include "node.h"
#include "segment.h"
#include "xattr.h"
#include "acl.h"
28
#include <trace/events/f2fs.h>
Jaegeuk Kim's avatar
Jaegeuk Kim committed
29
30
31
32
33

static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma,
						struct vm_fault *vmf)
{
	struct page *page = vmf->page;
34
	struct inode *inode = file_inode(vma->vm_file);
Jaegeuk Kim's avatar
Jaegeuk Kim committed
35
36
	struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
	struct dnode_of_data dn;
37
	int err;
Jaegeuk Kim's avatar
Jaegeuk Kim committed
38
39
40
41
42
43

	f2fs_balance_fs(sbi);

	sb_start_pagefault(inode->i_sb);

	/* block allocation */
44
	f2fs_lock_op(sbi);
Jaegeuk Kim's avatar
Jaegeuk Kim committed
45
	set_new_dnode(&dn, inode, NULL, NULL, 0);
46
	err = f2fs_reserve_block(&dn, page->index);
47
	f2fs_unlock_op(sbi);
48
49
	if (err)
		goto out;
Jaegeuk Kim's avatar
Jaegeuk Kim committed
50

51
	file_update_time(vma->vm_file);
Jaegeuk Kim's avatar
Jaegeuk Kim committed
52
	lock_page(page);
53
	if (unlikely(page->mapping != inode->i_mapping ||
54
			page_offset(page) > i_size_read(inode) ||
55
			!PageUptodate(page))) {
Jaegeuk Kim's avatar
Jaegeuk Kim committed
56
57
58
59
60
61
62
63
64
		unlock_page(page);
		err = -EFAULT;
		goto out;
	}

	/*
	 * check to see if the page is mapped already (no holes)
	 */
	if (PageMappedToDisk(page))
65
		goto mapped;
Jaegeuk Kim's avatar
Jaegeuk Kim committed
66
67
68
69
70
71
72
73
74
75

	/* page is wholly or partially inside EOF */
	if (((page->index + 1) << PAGE_CACHE_SHIFT) > i_size_read(inode)) {
		unsigned offset;
		offset = i_size_read(inode) & ~PAGE_CACHE_MASK;
		zero_user_segment(page, offset, PAGE_CACHE_SIZE);
	}
	set_page_dirty(page);
	SetPageUptodate(page);

76
	trace_f2fs_vm_page_mkwrite(page, DATA);
77
78
79
mapped:
	/* fill the page */
	wait_on_page_writeback(page);
Jaegeuk Kim's avatar
Jaegeuk Kim committed
80
81
82
83
84
85
out:
	sb_end_pagefault(inode->i_sb);
	return block_page_mkwrite_return(err);
}

static const struct vm_operations_struct f2fs_file_vm_ops = {
86
87
88
	.fault		= filemap_fault,
	.page_mkwrite	= f2fs_vm_page_mkwrite,
	.remap_pages	= generic_file_remap_pages,
Jaegeuk Kim's avatar
Jaegeuk Kim committed
89
90
};

91
92
93
94
95
96
97
98
99
100
static int get_parent_ino(struct inode *inode, nid_t *pino)
{
	struct dentry *dentry;

	inode = igrab(inode);
	dentry = d_find_any_alias(inode);
	iput(inode);
	if (!dentry)
		return 0;

101
102
103
104
	if (update_dent_inode(inode, &dentry->d_name)) {
		dput(dentry);
		return 0;
	}
105

106
107
	*pino = parent_ino(dentry);
	dput(dentry);
108
109
110
	return 1;
}

Jaegeuk Kim's avatar
Jaegeuk Kim committed
111
112
113
114
115
116
117
int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
{
	struct inode *inode = file->f_mapping->host;
	struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
	int ret = 0;
	bool need_cp = false;
	struct writeback_control wbc = {
118
		.sync_mode = WB_SYNC_NONE,
Jaegeuk Kim's avatar
Jaegeuk Kim committed
119
120
121
122
		.nr_to_write = LONG_MAX,
		.for_reclaim = 0,
	};

123
	if (unlikely(f2fs_readonly(inode->i_sb)))
124
125
		return 0;

126
	trace_f2fs_sync_file_enter(inode);
Jaegeuk Kim's avatar
Jaegeuk Kim committed
127
	ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
128
129
	if (ret) {
		trace_f2fs_sync_file_exit(inode, need_cp, datasync, ret);
Jaegeuk Kim's avatar
Jaegeuk Kim committed
130
		return ret;
131
	}
Jaegeuk Kim's avatar
Jaegeuk Kim committed
132

133
134
135
	/* guarantee free sections for fsync */
	f2fs_balance_fs(sbi);

Jaegeuk Kim's avatar
Jaegeuk Kim committed
136
137
	mutex_lock(&inode->i_mutex);

138
139
140
141
	/*
	 * Both of fdatasync() and fsync() are able to be recovered from
	 * sudden-power-off.
	 */
Jaegeuk Kim's avatar
Jaegeuk Kim committed
142
143
	if (!S_ISREG(inode->i_mode) || inode->i_nlink != 1)
		need_cp = true;
144
	else if (file_wrong_pino(inode))
Jaegeuk Kim's avatar
Jaegeuk Kim committed
145
		need_cp = true;
146
	else if (!space_for_roll_forward(sbi))
Jaegeuk Kim's avatar
Jaegeuk Kim committed
147
		need_cp = true;
148
	else if (!is_checkpointed_node(sbi, F2FS_I(inode)->i_pino))
Jaegeuk Kim's avatar
Jaegeuk Kim committed
149
		need_cp = true;
150
	else if (F2FS_I(inode)->xattr_ver == cur_cp_version(F2FS_CKPT(sbi)))
151
		need_cp = true;
Jaegeuk Kim's avatar
Jaegeuk Kim committed
152
153

	if (need_cp) {
154
155
		nid_t pino;

156
157
		F2FS_I(inode)->xattr_ver = 0;

Jaegeuk Kim's avatar
Jaegeuk Kim committed
158
159
		/* all the dirty node pages should be flushed for POR */
		ret = f2fs_sync_fs(inode->i_sb, 1);
160
161
162
163
164
165
166
167
168
		if (file_wrong_pino(inode) && inode->i_nlink == 1 &&
					get_parent_ino(inode, &pino)) {
			F2FS_I(inode)->i_pino = pino;
			file_got_pino(inode);
			mark_inode_dirty_sync(inode);
			ret = f2fs_write_inode(inode, NULL);
			if (ret)
				goto out;
		}
Jaegeuk Kim's avatar
Jaegeuk Kim committed
169
	} else {
170
171
		/* if there is no written node page, write its inode page */
		while (!sync_node_pages(sbi, inode->i_ino, &wbc)) {
172
			mark_inode_dirty_sync(inode);
173
174
175
176
			ret = f2fs_write_inode(inode, NULL);
			if (ret)
				goto out;
		}
177
178
179
		ret = wait_on_node_pages_writeback(sbi, inode->i_ino);
		if (ret)
			goto out;
180
		ret = blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL);
Jaegeuk Kim's avatar
Jaegeuk Kim committed
181
182
183
	}
out:
	mutex_unlock(&inode->i_mutex);
184
	trace_f2fs_sync_file_exit(inode, need_cp, datasync, ret);
Jaegeuk Kim's avatar
Jaegeuk Kim committed
185
186
187
188
189
190
191
192
193
194
	return ret;
}

static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma)
{
	file_accessed(file);
	vma->vm_ops = &f2fs_file_vm_ops;
	return 0;
}

195
int truncate_data_blocks_range(struct dnode_of_data *dn, int count)
Jaegeuk Kim's avatar
Jaegeuk Kim committed
196
197
198
199
200
201
{
	int nr_free = 0, ofs = dn->ofs_in_node;
	struct f2fs_sb_info *sbi = F2FS_SB(dn->inode->i_sb);
	struct f2fs_node *raw_node;
	__le32 *addr;

202
	raw_node = F2FS_NODE(dn->node_page);
Jaegeuk Kim's avatar
Jaegeuk Kim committed
203
204
205
206
207
208
209
210
211
212
213
214
	addr = blkaddr_in_node(raw_node) + ofs;

	for ( ; count > 0; count--, addr++, dn->ofs_in_node++) {
		block_t blkaddr = le32_to_cpu(*addr);
		if (blkaddr == NULL_ADDR)
			continue;

		update_extent_cache(NULL_ADDR, dn);
		invalidate_blocks(sbi, blkaddr);
		nr_free++;
	}
	if (nr_free) {
215
		dec_valid_block_count(sbi, dn->inode, nr_free);
Jaegeuk Kim's avatar
Jaegeuk Kim committed
216
217
218
219
		set_page_dirty(dn->node_page);
		sync_inode_page(dn);
	}
	dn->ofs_in_node = ofs;
220
221
222

	trace_f2fs_truncate_data_blocks_range(dn->inode, dn->nid,
					 dn->ofs_in_node, nr_free);
Jaegeuk Kim's avatar
Jaegeuk Kim committed
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
	return nr_free;
}

void truncate_data_blocks(struct dnode_of_data *dn)
{
	truncate_data_blocks_range(dn, ADDRS_PER_BLOCK);
}

static void truncate_partial_data_page(struct inode *inode, u64 from)
{
	unsigned offset = from & (PAGE_CACHE_SIZE - 1);
	struct page *page;

	if (!offset)
		return;

239
	page = find_data_page(inode, from >> PAGE_CACHE_SHIFT, false);
Jaegeuk Kim's avatar
Jaegeuk Kim committed
240
241
242
243
	if (IS_ERR(page))
		return;

	lock_page(page);
244
	if (unlikely(page->mapping != inode->i_mapping)) {
245
246
247
		f2fs_put_page(page, 1);
		return;
	}
Jaegeuk Kim's avatar
Jaegeuk Kim committed
248
249
250
251
252
253
	wait_on_page_writeback(page);
	zero_user(page, offset, PAGE_CACHE_SIZE - offset);
	set_page_dirty(page);
	f2fs_put_page(page, 1);
}

254
int truncate_blocks(struct inode *inode, u64 from)
Jaegeuk Kim's avatar
Jaegeuk Kim committed
255
256
257
258
259
{
	struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
	unsigned int blocksize = inode->i_sb->s_blocksize;
	struct dnode_of_data dn;
	pgoff_t free_from;
260
	int count = 0, err = 0;
Jaegeuk Kim's avatar
Jaegeuk Kim committed
261

262
263
	trace_f2fs_truncate_blocks_enter(inode, from);

264
265
266
	if (f2fs_has_inline_data(inode))
		goto done;

Jaegeuk Kim's avatar
Jaegeuk Kim committed
267
268
269
	free_from = (pgoff_t)
			((from + blocksize - 1) >> (sbi->log_blocksize));

270
	f2fs_lock_op(sbi);
271

Jaegeuk Kim's avatar
Jaegeuk Kim committed
272
	set_new_dnode(&dn, inode, NULL, NULL, 0);
273
	err = get_dnode_of_data(&dn, free_from, LOOKUP_NODE);
Jaegeuk Kim's avatar
Jaegeuk Kim committed
274
275
276
	if (err) {
		if (err == -ENOENT)
			goto free_next;
277
		f2fs_unlock_op(sbi);
278
		trace_f2fs_truncate_blocks_exit(inode, err);
Jaegeuk Kim's avatar
Jaegeuk Kim committed
279
280
281
282
		return err;
	}

	if (IS_INODE(dn.node_page))
283
		count = ADDRS_PER_INODE(F2FS_I(inode));
Jaegeuk Kim's avatar
Jaegeuk Kim committed
284
285
286
287
	else
		count = ADDRS_PER_BLOCK;

	count -= dn.ofs_in_node;
288
	f2fs_bug_on(count < 0);
289

Jaegeuk Kim's avatar
Jaegeuk Kim committed
290
291
292
293
294
295
296
297
	if (dn.ofs_in_node || IS_INODE(dn.node_page)) {
		truncate_data_blocks_range(&dn, count);
		free_from += count;
	}

	f2fs_put_dnode(&dn);
free_next:
	err = truncate_inode_blocks(inode, free_from);
298
	f2fs_unlock_op(sbi);
299
done:
Jaegeuk Kim's avatar
Jaegeuk Kim committed
300
301
302
	/* lastly zero out the first data page */
	truncate_partial_data_page(inode, from);

303
	trace_f2fs_truncate_blocks_exit(inode, err);
Jaegeuk Kim's avatar
Jaegeuk Kim committed
304
305
306
307
308
309
310
311
312
	return err;
}

void f2fs_truncate(struct inode *inode)
{
	if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
				S_ISLNK(inode->i_mode)))
		return;

313
314
	trace_f2fs_truncate(inode);

Jaegeuk Kim's avatar
Jaegeuk Kim committed
315
316
317
318
319
320
	if (!truncate_blocks(inode, i_size_read(inode))) {
		inode->i_mtime = inode->i_ctime = CURRENT_TIME;
		mark_inode_dirty(inode);
	}
}

321
int f2fs_getattr(struct vfsmount *mnt,
Jaegeuk Kim's avatar
Jaegeuk Kim committed
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
			 struct dentry *dentry, struct kstat *stat)
{
	struct inode *inode = dentry->d_inode;
	generic_fillattr(inode, stat);
	stat->blocks <<= 3;
	return 0;
}

#ifdef CONFIG_F2FS_FS_POSIX_ACL
static void __setattr_copy(struct inode *inode, const struct iattr *attr)
{
	struct f2fs_inode_info *fi = F2FS_I(inode);
	unsigned int ia_valid = attr->ia_valid;

	if (ia_valid & ATTR_UID)
		inode->i_uid = attr->ia_uid;
	if (ia_valid & ATTR_GID)
		inode->i_gid = attr->ia_gid;
	if (ia_valid & ATTR_ATIME)
		inode->i_atime = timespec_trunc(attr->ia_atime,
						inode->i_sb->s_time_gran);
	if (ia_valid & ATTR_MTIME)
		inode->i_mtime = timespec_trunc(attr->ia_mtime,
						inode->i_sb->s_time_gran);
	if (ia_valid & ATTR_CTIME)
		inode->i_ctime = timespec_trunc(attr->ia_ctime,
						inode->i_sb->s_time_gran);
	if (ia_valid & ATTR_MODE) {
		umode_t mode = attr->ia_mode;

		if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))
			mode &= ~S_ISGID;
		set_acl_inode(fi, mode);
	}
}
#else
#define __setattr_copy setattr_copy
#endif

int f2fs_setattr(struct dentry *dentry, struct iattr *attr)
{
	struct inode *inode = dentry->d_inode;
	struct f2fs_inode_info *fi = F2FS_I(inode);
	int err;

	err = inode_change_ok(inode, attr);
	if (err)
		return err;

	if ((attr->ia_valid & ATTR_SIZE) &&
			attr->ia_size != i_size_read(inode)) {
373
374
375
		err = f2fs_convert_inline_data(inode, attr->ia_size);
		if (err)
			return err;
376

Jaegeuk Kim's avatar
Jaegeuk Kim committed
377
		truncate_setsize(inode, attr->ia_size);
378
		f2fs_truncate(inode);
379
		f2fs_balance_fs(F2FS_SB(inode->i_sb));
Jaegeuk Kim's avatar
Jaegeuk Kim committed
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
	}

	__setattr_copy(inode, attr);

	if (attr->ia_valid & ATTR_MODE) {
		err = f2fs_acl_chmod(inode);
		if (err || is_inode_flag_set(fi, FI_ACL_MODE)) {
			inode->i_mode = fi->i_acl_mode;
			clear_inode_flag(fi, FI_ACL_MODE);
		}
	}

	mark_inode_dirty(inode);
	return err;
}

const struct inode_operations f2fs_file_inode_operations = {
	.getattr	= f2fs_getattr,
	.setattr	= f2fs_setattr,
	.get_acl	= f2fs_get_acl,
#ifdef CONFIG_F2FS_FS_XATTR
	.setxattr	= generic_setxattr,
	.getxattr	= generic_getxattr,
	.listxattr	= f2fs_listxattr,
	.removexattr	= generic_removexattr,
#endif
};

static void fill_zero(struct inode *inode, pgoff_t index,
					loff_t start, loff_t len)
{
411
	struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
Jaegeuk Kim's avatar
Jaegeuk Kim committed
412
413
414
415
416
	struct page *page;

	if (!len)
		return;

417
418
	f2fs_balance_fs(sbi);

419
	f2fs_lock_op(sbi);
420
	page = get_new_data_page(inode, NULL, index, false);
421
	f2fs_unlock_op(sbi);
Jaegeuk Kim's avatar
Jaegeuk Kim committed
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437

	if (!IS_ERR(page)) {
		wait_on_page_writeback(page);
		zero_user(page, start, len);
		set_page_dirty(page);
		f2fs_put_page(page, 1);
	}
}

int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end)
{
	pgoff_t index;
	int err;

	for (index = pg_start; index < pg_end; index++) {
		struct dnode_of_data dn;
438

Jaegeuk Kim's avatar
Jaegeuk Kim committed
439
		set_new_dnode(&dn, inode, NULL, NULL, 0);
440
		err = get_dnode_of_data(&dn, index, LOOKUP_NODE);
Jaegeuk Kim's avatar
Jaegeuk Kim committed
441
442
443
444
445
446
447
448
449
450
451
452
453
		if (err) {
			if (err == -ENOENT)
				continue;
			return err;
		}

		if (dn.data_blkaddr != NULL_ADDR)
			truncate_data_blocks_range(&dn, 1);
		f2fs_put_dnode(&dn);
	}
	return 0;
}

454
static int punch_hole(struct inode *inode, loff_t offset, loff_t len)
Jaegeuk Kim's avatar
Jaegeuk Kim committed
455
456
457
458
459
{
	pgoff_t pg_start, pg_end;
	loff_t off_start, off_end;
	int ret = 0;

460
	ret = f2fs_convert_inline_data(inode, MAX_INLINE_DATA + 1);
461
462
	if (ret)
		return ret;
463

Jaegeuk Kim's avatar
Jaegeuk Kim committed
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
	pg_start = ((unsigned long long) offset) >> PAGE_CACHE_SHIFT;
	pg_end = ((unsigned long long) offset + len) >> PAGE_CACHE_SHIFT;

	off_start = offset & (PAGE_CACHE_SIZE - 1);
	off_end = (offset + len) & (PAGE_CACHE_SIZE - 1);

	if (pg_start == pg_end) {
		fill_zero(inode, pg_start, off_start,
						off_end - off_start);
	} else {
		if (off_start)
			fill_zero(inode, pg_start++, off_start,
					PAGE_CACHE_SIZE - off_start);
		if (off_end)
			fill_zero(inode, pg_end, 0, off_end);

		if (pg_start < pg_end) {
			struct address_space *mapping = inode->i_mapping;
			loff_t blk_start, blk_end;
483
484
485
			struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);

			f2fs_balance_fs(sbi);
Jaegeuk Kim's avatar
Jaegeuk Kim committed
486
487
488
489
490

			blk_start = pg_start << PAGE_CACHE_SHIFT;
			blk_end = pg_end << PAGE_CACHE_SHIFT;
			truncate_inode_pages_range(mapping, blk_start,
					blk_end - 1);
491

492
			f2fs_lock_op(sbi);
Jaegeuk Kim's avatar
Jaegeuk Kim committed
493
			ret = truncate_hole(inode, pg_start, pg_end);
494
			f2fs_unlock_op(sbi);
Jaegeuk Kim's avatar
Jaegeuk Kim committed
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
		}
	}

	return ret;
}

static int expand_inode_data(struct inode *inode, loff_t offset,
					loff_t len, int mode)
{
	struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);
	pgoff_t index, pg_start, pg_end;
	loff_t new_size = i_size_read(inode);
	loff_t off_start, off_end;
	int ret = 0;

	ret = inode_newsize_ok(inode, (len + offset));
	if (ret)
		return ret;

514
515
516
517
	ret = f2fs_convert_inline_data(inode, offset + len);
	if (ret)
		return ret;

Jaegeuk Kim's avatar
Jaegeuk Kim committed
518
519
520
521
522
523
524
525
526
	pg_start = ((unsigned long long) offset) >> PAGE_CACHE_SHIFT;
	pg_end = ((unsigned long long) offset + len) >> PAGE_CACHE_SHIFT;

	off_start = offset & (PAGE_CACHE_SIZE - 1);
	off_end = (offset + len) & (PAGE_CACHE_SIZE - 1);

	for (index = pg_start; index <= pg_end; index++) {
		struct dnode_of_data dn;

527
		f2fs_lock_op(sbi);
Jaegeuk Kim's avatar
Jaegeuk Kim committed
528
		set_new_dnode(&dn, inode, NULL, NULL, 0);
529
530
531
		ret = f2fs_reserve_block(&dn, index);
		f2fs_unlock_op(sbi);
		if (ret)
Jaegeuk Kim's avatar
Jaegeuk Kim committed
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
			break;

		if (pg_start == pg_end)
			new_size = offset + len;
		else if (index == pg_start && off_start)
			new_size = (index + 1) << PAGE_CACHE_SHIFT;
		else if (index == pg_end)
			new_size = (index << PAGE_CACHE_SHIFT) + off_end;
		else
			new_size += PAGE_CACHE_SIZE;
	}

	if (!(mode & FALLOC_FL_KEEP_SIZE) &&
		i_size_read(inode) < new_size) {
		i_size_write(inode, new_size);
		mark_inode_dirty(inode);
	}

	return ret;
}

static long f2fs_fallocate(struct file *file, int mode,
				loff_t offset, loff_t len)
{
556
	struct inode *inode = file_inode(file);
Jaegeuk Kim's avatar
Jaegeuk Kim committed
557
558
559
560
561
562
	long ret;

	if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE))
		return -EOPNOTSUPP;

	if (mode & FALLOC_FL_PUNCH_HOLE)
563
		ret = punch_hole(inode, offset, len);
Jaegeuk Kim's avatar
Jaegeuk Kim committed
564
565
566
	else
		ret = expand_inode_data(inode, offset, len, mode);

567
568
569
570
	if (!ret) {
		inode->i_mtime = inode->i_ctime = CURRENT_TIME;
		mark_inode_dirty(inode);
	}
571
	trace_f2fs_fallocate(inode, mode, offset, len, ret);
Jaegeuk Kim's avatar
Jaegeuk Kim committed
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
	return ret;
}

#define F2FS_REG_FLMASK		(~(FS_DIRSYNC_FL | FS_TOPDIR_FL))
#define F2FS_OTHER_FLMASK	(FS_NODUMP_FL | FS_NOATIME_FL)

static inline __u32 f2fs_mask_flags(umode_t mode, __u32 flags)
{
	if (S_ISDIR(mode))
		return flags;
	else if (S_ISREG(mode))
		return flags & F2FS_REG_FLMASK;
	else
		return flags & F2FS_OTHER_FLMASK;
}

long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
590
	struct inode *inode = file_inode(filp);
Jaegeuk Kim's avatar
Jaegeuk Kim committed
591
592
593
594
595
	struct f2fs_inode_info *fi = F2FS_I(inode);
	unsigned int flags;
	int ret;

	switch (cmd) {
596
	case F2FS_IOC_GETFLAGS:
Jaegeuk Kim's avatar
Jaegeuk Kim committed
597
598
		flags = fi->i_flags & FS_FL_USER_VISIBLE;
		return put_user(flags, (int __user *) arg);
599
	case F2FS_IOC_SETFLAGS:
Jaegeuk Kim's avatar
Jaegeuk Kim committed
600
601
602
	{
		unsigned int oldflags;

603
		ret = mnt_want_write_file(filp);
Jaegeuk Kim's avatar
Jaegeuk Kim committed
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
		if (ret)
			return ret;

		if (!inode_owner_or_capable(inode)) {
			ret = -EACCES;
			goto out;
		}

		if (get_user(flags, (int __user *) arg)) {
			ret = -EFAULT;
			goto out;
		}

		flags = f2fs_mask_flags(inode->i_mode, flags);

		mutex_lock(&inode->i_mutex);

		oldflags = fi->i_flags;

		if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
			if (!capable(CAP_LINUX_IMMUTABLE)) {
				mutex_unlock(&inode->i_mutex);
				ret = -EPERM;
				goto out;
			}
		}

		flags = flags & FS_FL_USER_MODIFIABLE;
		flags |= oldflags & ~FS_FL_USER_MODIFIABLE;
		fi->i_flags = flags;
		mutex_unlock(&inode->i_mutex);

		f2fs_set_inode_flags(inode);
		inode->i_ctime = CURRENT_TIME;
		mark_inode_dirty(inode);
out:
640
		mnt_drop_write_file(filp);
Jaegeuk Kim's avatar
Jaegeuk Kim committed
641
642
643
644
645
646
647
		return ret;
	}
	default:
		return -ENOTTY;
	}
}

648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
#ifdef CONFIG_COMPAT
long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	switch (cmd) {
	case F2FS_IOC32_GETFLAGS:
		cmd = F2FS_IOC_GETFLAGS;
		break;
	case F2FS_IOC32_SETFLAGS:
		cmd = F2FS_IOC_SETFLAGS;
		break;
	default:
		return -ENOIOCTLCMD;
	}
	return f2fs_ioctl(file, cmd, (unsigned long) compat_ptr(arg));
}
#endif

Jaegeuk Kim's avatar
Jaegeuk Kim committed
665
666
667
668
669
670
671
672
673
674
675
const struct file_operations f2fs_file_operations = {
	.llseek		= generic_file_llseek,
	.read		= do_sync_read,
	.write		= do_sync_write,
	.aio_read	= generic_file_aio_read,
	.aio_write	= generic_file_aio_write,
	.open		= generic_file_open,
	.mmap		= f2fs_file_mmap,
	.fsync		= f2fs_sync_file,
	.fallocate	= f2fs_fallocate,
	.unlocked_ioctl	= f2fs_ioctl,
676
677
678
#ifdef CONFIG_COMPAT
	.compat_ioctl	= f2fs_compat_ioctl,
#endif
Jaegeuk Kim's avatar
Jaegeuk Kim committed
679
680
681
	.splice_read	= generic_file_splice_read,
	.splice_write	= generic_file_splice_write,
};