Blame view

fs/xfs/xfs_utils.c 8.2 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
  /*
7b7187698   Nathan Scott   [XFS] Update lice...
2
3
   * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc.
   * All Rights Reserved.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
4
   *
7b7187698   Nathan Scott   [XFS] Update lice...
5
6
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License as
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
7
8
   * published by the Free Software Foundation.
   *
7b7187698   Nathan Scott   [XFS] Update lice...
9
10
11
12
   * This program is distributed in the hope that it would be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
   *
7b7187698   Nathan Scott   [XFS] Update lice...
14
15
16
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write the Free Software Foundation,
   * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
  #include "xfs.h"
a844f4510   Nathan Scott   [XFS] Remove xfs_...
19
  #include "xfs_fs.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
20
  #include "xfs_types.h"
a844f4510   Nathan Scott   [XFS] Remove xfs_...
21
  #include "xfs_bit.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
  #include "xfs_log.h"
a844f4510   Nathan Scott   [XFS] Remove xfs_...
23
  #include "xfs_inum.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24
25
  #include "xfs_trans.h"
  #include "xfs_sb.h"
a844f4510   Nathan Scott   [XFS] Remove xfs_...
26
  #include "xfs_ag.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
  #include "xfs_dir2.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28
29
  #include "xfs_mount.h"
  #include "xfs_bmap_btree.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
  #include "xfs_dinode.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
31
  #include "xfs_inode.h"
a844f4510   Nathan Scott   [XFS] Remove xfs_...
32
  #include "xfs_inode_item.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33
34
35
  #include "xfs_bmap.h"
  #include "xfs_error.h"
  #include "xfs_quota.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36
37
  #include "xfs_itable.h"
  #include "xfs_utils.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
  /*
   * Allocates a new inode from disk and return a pointer to the
   * incore copy. This routine will internally commit the current
   * transaction and allocate a new one if the Space Manager needed
   * to do an allocation to replenish the inode free-list.
   *
   * This routine is designed to be called from xfs_create and
   * xfs_create_dir.
   *
   */
  int
  xfs_dir_ialloc(
  	xfs_trans_t	**tpp,		/* input: current transaction;
  					   output: may be a new transaction. */
  	xfs_inode_t	*dp,		/* directory within whose allocate
  					   the inode. */
  	mode_t		mode,
31b084aef   Nathan Scott   [XFS] Fix up uses...
56
  	xfs_nlink_t	nlink,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
57
  	xfs_dev_t	rdev,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
  	prid_t		prid,		/* project id */
  	int		okalloc,	/* ok to allocate new space */
  	xfs_inode_t	**ipp,		/* pointer to inode; it will be
  					   locked. */
  	int		*committed)
  
  {
  	xfs_trans_t	*tp;
  	xfs_trans_t	*ntp;
  	xfs_inode_t	*ip;
  	xfs_buf_t	*ialloc_context = NULL;
  	boolean_t	call_again = B_FALSE;
  	int		code;
  	uint		log_res;
  	uint		log_count;
  	void		*dqinfo;
  	uint		tflags;
  
  	tp = *tpp;
  	ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES);
  
  	/*
  	 * xfs_ialloc will return a pointer to an incore inode if
  	 * the Space Manager has an available inode on the free
  	 * list. Otherwise, it will do an allocation and replenish
  	 * the freelist.  Since we can only do one allocation per
  	 * transaction without deadlocks, we will need to commit the
  	 * current transaction and start a new one.  We will then
  	 * need to call xfs_ialloc again to get the inode.
  	 *
  	 * If xfs_ialloc did an allocation to replenish the freelist,
  	 * it returns the bp containing the head of the freelist as
  	 * ialloc_context. We will hold a lock on it across the
  	 * transaction commit so that no other process can steal
  	 * the inode(s) that we've just allocated.
  	 */
6c77b0ea1   Christoph Hellwig   xfs: remove xfs_c...
94
  	code = xfs_ialloc(tp, dp, mode, nlink, rdev, prid, okalloc,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
  			  &ialloc_context, &call_again, &ip);
  
  	/*
  	 * Return an error if we were unable to allocate a new inode.
  	 * This should only happen if we run out of space on disk or
  	 * encounter a disk error.
  	 */
  	if (code) {
  		*ipp = NULL;
  		return code;
  	}
  	if (!call_again && (ip == NULL)) {
  		*ipp = NULL;
  		return XFS_ERROR(ENOSPC);
  	}
  
  	/*
  	 * If call_again is set, then we were unable to get an
  	 * inode in one operation.  We need to commit the current
  	 * transaction and call xfs_ialloc() again.  It is guaranteed
  	 * to succeed the second time.
  	 */
  	if (call_again) {
  
  		/*
  		 * Normally, xfs_trans_commit releases all the locks.
  		 * We call bhold to hang on to the ialloc_context across
  		 * the commit.  Holding this buffer prevents any other
  		 * processes from doing any allocations in this
  		 * allocation group.
  		 */
  		xfs_trans_bhold(tp, ialloc_context);
  		/*
  		 * Save the log reservation so we can use
  		 * them in the next transaction.
  		 */
  		log_res = xfs_trans_get_log_res(tp);
  		log_count = xfs_trans_get_log_count(tp);
  
  		/*
  		 * We want the quota changes to be associated with the next
  		 * transaction, NOT this one. So, detach the dqinfo from this
  		 * and attach it to the next transaction.
  		 */
  		dqinfo = NULL;
  		tflags = 0;
  		if (tp->t_dqinfo) {
  			dqinfo = (void *)tp->t_dqinfo;
  			tp->t_dqinfo = NULL;
  			tflags = tp->t_flags & XFS_TRANS_DQ_DIRTY;
  			tp->t_flags &= ~(XFS_TRANS_DQ_DIRTY);
  		}
  
  		ntp = xfs_trans_dup(tp);
1c72bf900   Eric Sandeen   [XFS] The last ar...
149
  		code = xfs_trans_commit(tp, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
150
151
152
153
154
155
156
157
158
159
160
161
162
  		tp = ntp;
  		if (committed != NULL) {
  			*committed = 1;
  		}
  		/*
  		 * If we get an error during the commit processing,
  		 * release the buffer that is still held and return
  		 * to the caller.
  		 */
  		if (code) {
  			xfs_buf_relse(ialloc_context);
  			if (dqinfo) {
  				tp->t_dqinfo = dqinfo;
7d095257e   Christoph Hellwig   xfs: kill xfs_qmops
163
  				xfs_trans_free_dqinfo(tp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
164
165
166
167
168
  			}
  			*tpp = ntp;
  			*ipp = NULL;
  			return code;
  		}
cc09c0dc5   Dave Chinner   [XFS] Fix double ...
169
170
171
172
173
174
  
  		/*
  		 * transaction commit worked ok so we can drop the extra ticket
  		 * reference that we gained in xfs_trans_dup()
  		 */
  		xfs_log_ticket_put(tp->t_ticket);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
  		code = xfs_trans_reserve(tp, 0, log_res, 0,
  					 XFS_TRANS_PERM_LOG_RES, log_count);
  		/*
  		 * Re-attach the quota info that we detached from prev trx.
  		 */
  		if (dqinfo) {
  			tp->t_dqinfo = dqinfo;
  			tp->t_flags |= tflags;
  		}
  
  		if (code) {
  			xfs_buf_relse(ialloc_context);
  			*tpp = ntp;
  			*ipp = NULL;
  			return code;
  		}
  		xfs_trans_bjoin(tp, ialloc_context);
  
  		/*
  		 * Call ialloc again. Since we've locked out all
  		 * other allocations in this allocation group,
  		 * this call should always succeed.
  		 */
6c77b0ea1   Christoph Hellwig   xfs: remove xfs_c...
198
  		code = xfs_ialloc(tp, dp, mode, nlink, rdev, prid,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
  				  okalloc, &ialloc_context, &call_again, &ip);
  
  		/*
  		 * If we get an error at this point, return to the caller
  		 * so that the current transaction can be aborted.
  		 */
  		if (code) {
  			*tpp = tp;
  			*ipp = NULL;
  			return code;
  		}
  		ASSERT ((!call_again) && (ip != NULL));
  
  	} else {
  		if (committed != NULL) {
  			*committed = 0;
  		}
  	}
  
  	*ipp = ip;
  	*tpp = tp;
  
  	return 0;
  }
  
  /*
   * Decrement the link count on an inode & log the change.
   * If this causes the link count to go to zero, initiate the
   * logging activity required to truncate a file.
   */
  int				/* error */
  xfs_droplink(
  	xfs_trans_t *tp,
  	xfs_inode_t *ip)
  {
  	int	error;
dcd79a142   Dave Chinner   xfs: don't use vf...
235
  	xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
236
237
238
  
  	ASSERT (ip->i_d.di_nlink > 0);
  	ip->i_d.di_nlink--;
016516462   David Chinner   [XFS] Avoid direc...
239
  	drop_nlink(VFS_I(ip));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
  	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
  
  	error = 0;
  	if (ip->i_d.di_nlink == 0) {
  		/*
  		 * We're dropping the last link to this file.
  		 * Move the on-disk inode to the AGI unlinked list.
  		 * From xfs_inactive() we will pull the inode from
  		 * the list and free it.
  		 */
  		error = xfs_iunlink(tp, ip);
  	}
  	return error;
  }
  
  /*
   * This gets called when the inode's version needs to be changed from 1 to 2.
   * Currently this happens when the nlink field overflows the old 16-bit value
   * or when chproj is called to change the project for the first time.
   * As a side effect the superblock version will also get rev'd
   * to contain the NLINK bit.
   */
  void
  xfs_bump_ino_vers2(
  	xfs_trans_t	*tp,
  	xfs_inode_t	*ip)
  {
  	xfs_mount_t	*mp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
268

579aa9caf   Christoph Hellwig   [XFS] shrink mrlo...
269
  	ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
51ce16d51   Christoph Hellwig   [XFS] kill XFS_DI...
270
  	ASSERT(ip->i_d.di_version == 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
271

51ce16d51   Christoph Hellwig   [XFS] kill XFS_DI...
272
  	ip->i_d.di_version = 2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273
274
275
  	ip->i_d.di_onlink = 0;
  	memset(&(ip->i_d.di_pad[0]), 0, sizeof(ip->i_d.di_pad));
  	mp = tp->t_mountp;
621187099   Eric Sandeen   [XFS] remove shou...
276
  	if (!xfs_sb_version_hasnlink(&mp->m_sb)) {
3685c2a1d   Eric Sandeen   [XFS] Unwrap XFS_...
277
  		spin_lock(&mp->m_sb_lock);
621187099   Eric Sandeen   [XFS] remove shou...
278
279
  		if (!xfs_sb_version_hasnlink(&mp->m_sb)) {
  			xfs_sb_version_addnlink(&mp->m_sb);
3685c2a1d   Eric Sandeen   [XFS] Unwrap XFS_...
280
  			spin_unlock(&mp->m_sb_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281
282
  			xfs_mod_sb(tp, XFS_SB_VERSIONNUM);
  		} else {
3685c2a1d   Eric Sandeen   [XFS] Unwrap XFS_...
283
  			spin_unlock(&mp->m_sb_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
  		}
  	}
  	/* Caller must log the inode */
  }
  
  /*
   * Increment the link count on an inode & log the change.
   */
  int
  xfs_bumplink(
  	xfs_trans_t *tp,
  	xfs_inode_t *ip)
  {
  	if (ip->i_d.di_nlink >= XFS_MAXLINK)
  		return XFS_ERROR(EMLINK);
dcd79a142   Dave Chinner   xfs: don't use vf...
299
  	xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
300
301
302
  
  	ASSERT(ip->i_d.di_nlink > 0);
  	ip->i_d.di_nlink++;
016516462   David Chinner   [XFS] Avoid direc...
303
  	inc_nlink(VFS_I(ip));
51ce16d51   Christoph Hellwig   [XFS] kill XFS_DI...
304
  	if ((ip->i_d.di_version == 1) &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
  	    (ip->i_d.di_nlink > XFS_MAXLINK_1)) {
  		/*
  		 * The inode has increased its number of links beyond
  		 * what can fit in an old format inode.  It now needs
  		 * to be converted to a version 2 inode with a 32 bit
  		 * link count.  If this is the first inode in the file
  		 * system to do this, then we need to bump the superblock
  		 * version number as well.
  		 */
  		xfs_bump_ino_vers2(tp, ip);
  	}
  
  	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
  	return 0;
  }