1/*
2 *   Copyright (c) International Business Machines Corp., 2000-2002
3 *   Portions Copyright (c) Christoph Hellwig, 2001-2002
4 *
5 *   This program is free software;  you can redistribute it and/or modify
6 *   it under the terms of the GNU General Public License as published by
7 *   the Free Software Foundation; either version 2 of the License, or
8 *   (at your option) any later version.
9 *
10 *   This program is distributed in the hope that it will be useful,
11 *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
12 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
13 *   the GNU General Public License for more details.
14 *
15 *   You should have received a copy of the GNU General Public License
16 *   along with this program;  if not, write to the Free Software
17 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20#include <linux/fs.h>
21#include "jfs_incore.h"
22#include "jfs_dmap.h"
23#include "jfs_txnmgr.h"
24#include "jfs_xattr.h"
25#include "jfs_debug.h"
26
27
28extern int jfs_commit_inode(struct inode *, int);
29
30int jfs_fsync(struct file *file, struct dentry *dentry, int datasync)
31{
32	struct inode *inode = dentry->d_inode;
33	int rc = 0;
34
35	rc = fsync_inode_data_buffers(inode);
36
37	if (!(inode->i_state & I_DIRTY))
38		return rc;
39	if (datasync && !(inode->i_state & I_DIRTY_DATASYNC))
40		return rc;
41
42	rc |= jfs_commit_inode(inode, 1);
43
44	return rc ? -EIO : 0;
45}
46
47/*
48 * Guts of jfs_truncate.  Called with locks already held.  Can be called
49 * with directory for truncating directory index table.
50 */
51void jfs_truncate_nolock(struct inode *ip, loff_t length)
52{
53	loff_t newsize;
54	tid_t tid;
55
56	ASSERT(length >= 0);
57
58	if (test_cflag(COMMIT_Nolink, ip)) {
59		xtTruncate(0, ip, length, COMMIT_WMAP);
60		return;
61	}
62
63	do {
64		tid = txBegin(ip->i_sb, 0);
65
66		/*
67		 * The commit_sem cannot be taken before txBegin.
68		 * txBegin may block and there is a chance the inode
69		 * could be marked dirty and need to be committed
70		 * before txBegin unblocks
71		 */
72		down(&JFS_IP(ip)->commit_sem);
73
74		newsize = xtTruncate(tid, ip, length,
75				     COMMIT_TRUNCATE | COMMIT_PWMAP);
76		if (newsize < 0) {
77			txEnd(tid);
78			up(&JFS_IP(ip)->commit_sem);
79			break;
80		}
81
82		ip->i_mtime = ip->i_ctime = CURRENT_TIME;
83		mark_inode_dirty(ip);
84
85		txCommit(tid, 1, &ip, 0);
86		txEnd(tid);
87		up(&JFS_IP(ip)->commit_sem);
88	} while (newsize > length);	/* Truncate isn't always atomic */
89}
90
91static void jfs_truncate(struct inode *ip)
92{
93	jFYI(1, ("jfs_truncate: size = 0x%lx\n", (ulong) ip->i_size));
94
95	IWRITE_LOCK(ip);
96	jfs_truncate_nolock(ip, ip->i_size);
97	IWRITE_UNLOCK(ip);
98}
99
100static int jfs_open(struct inode *inode, struct file *file)
101{
102	int rc;
103
104	if ((rc = generic_file_open(inode, file)))
105		return rc;
106
107	/*
108	 * We attempt to allow only one "active" file open per aggregate
109	 * group.  Otherwise, appending to files in parallel can cause
110	 * fragmentation within the files.
111	 *
112	 * If the file is empty, it was probably just created and going
113	 * to be written to.  If it has a size, we'll hold off until the
114	 * file is actually grown.
115	 */
116	if (S_ISREG(inode->i_mode) && file->f_mode & FMODE_WRITE &&
117	    (inode->i_size == 0)) {
118		struct jfs_inode_info *ji = JFS_IP(inode);
119		if (ji->active_ag == -1) {
120			ji->active_ag = ji->agno;
121			atomic_inc(
122			    &JFS_SBI(inode->i_sb)->bmap->db_active[ji->agno]);
123		}
124	}
125
126	return 0;
127}
128static int jfs_release(struct inode *inode, struct file *file)
129{
130	struct jfs_inode_info *ji = JFS_IP(inode);
131
132	if (ji->active_ag != -1) {
133		struct bmap *bmap = JFS_SBI(inode->i_sb)->bmap;
134		atomic_dec(&bmap->db_active[ji->active_ag]);
135		ji->active_ag = -1;
136	}
137
138	return 0;
139}
140
141struct inode_operations jfs_file_inode_operations = {
142	.truncate	= jfs_truncate,
143	.setxattr	= jfs_setxattr,
144	.getxattr	= jfs_getxattr,
145	.listxattr	= jfs_listxattr,
146	.removexattr	= jfs_removexattr,
147};
148
149struct file_operations jfs_file_operations = {
150	.open		= jfs_open,
151	.llseek		= generic_file_llseek,
152	.write		= generic_file_write,
153	.read		= generic_file_read,
154	.mmap		= generic_file_mmap,
155	.fsync		= jfs_fsync,
156	.release	= jfs_release,
157};
158