diff options
Diffstat (limited to 'fs/xfs/xfs_dfrag.c')
-rw-r--r-- | fs/xfs/xfs_dfrag.c | 387 |
1 files changed, 387 insertions, 0 deletions
diff --git a/fs/xfs/xfs_dfrag.c b/fs/xfs/xfs_dfrag.c new file mode 100644 index 000000000000..08d551a17347 --- /dev/null +++ b/fs/xfs/xfs_dfrag.c | |||
@@ -0,0 +1,387 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of version 2 of the GNU General Public License as | ||
6 | * published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it would be useful, but | ||
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | * | ||
12 | * Further, this software is distributed without any warranty that it is | ||
13 | * free of the rightful claim of any third person regarding infringement | ||
14 | * or the like. Any license provided herein, whether implied or | ||
15 | * otherwise, applies only to this software file. Patent licenses, if | ||
16 | * any, provided herein do not apply to combinations of this program with | ||
17 | * other software, or any other product whatsoever. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License along | ||
20 | * with this program; if not, write the Free Software Foundation, Inc., 59 | ||
21 | * Temple Place - Suite 330, Boston MA 02111-1307, USA. | ||
22 | * | ||
23 | * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, | ||
24 | * Mountain View, CA 94043, or: | ||
25 | * | ||
26 | * http://www.sgi.com | ||
27 | * | ||
28 | * For further information regarding this notice, see: | ||
29 | * | ||
30 | * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ | ||
31 | */ | ||
32 | |||
33 | #include "xfs.h" | ||
34 | #include "xfs_macros.h" | ||
35 | #include "xfs_types.h" | ||
36 | #include "xfs_inum.h" | ||
37 | #include "xfs_log.h" | ||
38 | #include "xfs_trans.h" | ||
39 | #include "xfs_sb.h" | ||
40 | #include "xfs_dir.h" | ||
41 | #include "xfs_dir2.h" | ||
42 | #include "xfs_dmapi.h" | ||
43 | #include "xfs_mount.h" | ||
44 | #include "xfs_ag.h" | ||
45 | #include "xfs_alloc_btree.h" | ||
46 | #include "xfs_bmap_btree.h" | ||
47 | #include "xfs_ialloc_btree.h" | ||
48 | #include "xfs_btree.h" | ||
49 | #include "xfs_attr_sf.h" | ||
50 | #include "xfs_dir_sf.h" | ||
51 | #include "xfs_dir2_sf.h" | ||
52 | #include "xfs_dinode.h" | ||
53 | #include "xfs_inode_item.h" | ||
54 | #include "xfs_inode.h" | ||
55 | #include "xfs_bmap.h" | ||
56 | #include "xfs_ialloc.h" | ||
57 | #include "xfs_itable.h" | ||
58 | #include "xfs_dfrag.h" | ||
59 | #include "xfs_error.h" | ||
60 | #include "xfs_mac.h" | ||
61 | #include "xfs_rw.h" | ||
62 | |||
63 | /* | ||
64 | * Syssgi interface for swapext | ||
65 | */ | ||
66 | int | ||
67 | xfs_swapext( | ||
68 | xfs_swapext_t __user *sxp) | ||
69 | { | ||
70 | xfs_swapext_t sx; | ||
71 | xfs_inode_t *ip=NULL, *tip=NULL, *ips[2]; | ||
72 | xfs_trans_t *tp; | ||
73 | xfs_mount_t *mp; | ||
74 | xfs_bstat_t *sbp; | ||
75 | struct file *fp = NULL, *tfp = NULL; | ||
76 | vnode_t *vp, *tvp; | ||
77 | bhv_desc_t *bdp, *tbdp; | ||
78 | vn_bhv_head_t *bhp, *tbhp; | ||
79 | uint lock_flags=0; | ||
80 | int ilf_fields, tilf_fields; | ||
81 | int error = 0; | ||
82 | xfs_ifork_t tempif, *ifp, *tifp; | ||
83 | __uint64_t tmp; | ||
84 | int aforkblks = 0; | ||
85 | int taforkblks = 0; | ||
86 | int locked = 0; | ||
87 | |||
88 | if (copy_from_user(&sx, sxp, sizeof(sx))) | ||
89 | return XFS_ERROR(EFAULT); | ||
90 | |||
91 | /* Pull information for the target fd */ | ||
92 | if (((fp = fget((int)sx.sx_fdtarget)) == NULL) || | ||
93 | ((vp = LINVFS_GET_VP(fp->f_dentry->d_inode)) == NULL)) { | ||
94 | error = XFS_ERROR(EINVAL); | ||
95 | goto error0; | ||
96 | } | ||
97 | |||
98 | bhp = VN_BHV_HEAD(vp); | ||
99 | bdp = vn_bhv_lookup(bhp, &xfs_vnodeops); | ||
100 | if (bdp == NULL) { | ||
101 | error = XFS_ERROR(EBADF); | ||
102 | goto error0; | ||
103 | } else { | ||
104 | ip = XFS_BHVTOI(bdp); | ||
105 | } | ||
106 | |||
107 | if (((tfp = fget((int)sx.sx_fdtmp)) == NULL) || | ||
108 | ((tvp = LINVFS_GET_VP(tfp->f_dentry->d_inode)) == NULL)) { | ||
109 | error = XFS_ERROR(EINVAL); | ||
110 | goto error0; | ||
111 | } | ||
112 | |||
113 | tbhp = VN_BHV_HEAD(tvp); | ||
114 | tbdp = vn_bhv_lookup(tbhp, &xfs_vnodeops); | ||
115 | if (tbdp == NULL) { | ||
116 | error = XFS_ERROR(EBADF); | ||
117 | goto error0; | ||
118 | } else { | ||
119 | tip = XFS_BHVTOI(tbdp); | ||
120 | } | ||
121 | |||
122 | if (ip->i_mount != tip->i_mount) { | ||
123 | error = XFS_ERROR(EINVAL); | ||
124 | goto error0; | ||
125 | } | ||
126 | |||
127 | if (ip->i_ino == tip->i_ino) { | ||
128 | error = XFS_ERROR(EINVAL); | ||
129 | goto error0; | ||
130 | } | ||
131 | |||
132 | mp = ip->i_mount; | ||
133 | |||
134 | sbp = &sx.sx_stat; | ||
135 | |||
136 | if (XFS_FORCED_SHUTDOWN(mp)) { | ||
137 | error = XFS_ERROR(EIO); | ||
138 | goto error0; | ||
139 | } | ||
140 | |||
141 | locked = 1; | ||
142 | |||
143 | /* Lock in i_ino order */ | ||
144 | if (ip->i_ino < tip->i_ino) { | ||
145 | ips[0] = ip; | ||
146 | ips[1] = tip; | ||
147 | } else { | ||
148 | ips[0] = tip; | ||
149 | ips[1] = ip; | ||
150 | } | ||
151 | lock_flags = XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL; | ||
152 | xfs_lock_inodes(ips, 2, 0, lock_flags); | ||
153 | |||
154 | /* Check permissions */ | ||
155 | error = xfs_iaccess(ip, S_IWUSR, NULL); | ||
156 | if (error) | ||
157 | goto error0; | ||
158 | |||
159 | error = xfs_iaccess(tip, S_IWUSR, NULL); | ||
160 | if (error) | ||
161 | goto error0; | ||
162 | |||
163 | /* Verify that both files have the same format */ | ||
164 | if ((ip->i_d.di_mode & S_IFMT) != (tip->i_d.di_mode & S_IFMT)) { | ||
165 | error = XFS_ERROR(EINVAL); | ||
166 | goto error0; | ||
167 | } | ||
168 | |||
169 | /* Verify both files are either real-time or non-realtime */ | ||
170 | if ((ip->i_d.di_flags & XFS_DIFLAG_REALTIME) != | ||
171 | (tip->i_d.di_flags & XFS_DIFLAG_REALTIME)) { | ||
172 | error = XFS_ERROR(EINVAL); | ||
173 | goto error0; | ||
174 | } | ||
175 | |||
176 | /* Should never get a local format */ | ||
177 | if (ip->i_d.di_format == XFS_DINODE_FMT_LOCAL || | ||
178 | tip->i_d.di_format == XFS_DINODE_FMT_LOCAL) { | ||
179 | error = XFS_ERROR(EINVAL); | ||
180 | goto error0; | ||
181 | } | ||
182 | |||
183 | if (VN_CACHED(tvp) != 0) | ||
184 | xfs_inval_cached_pages(XFS_ITOV(tip), &(tip->i_iocore), | ||
185 | (loff_t)0, 0, 0); | ||
186 | |||
187 | /* Verify O_DIRECT for ftmp */ | ||
188 | if (VN_CACHED(tvp) != 0) { | ||
189 | error = XFS_ERROR(EINVAL); | ||
190 | goto error0; | ||
191 | } | ||
192 | |||
193 | /* Verify all data are being swapped */ | ||
194 | if (sx.sx_offset != 0 || | ||
195 | sx.sx_length != ip->i_d.di_size || | ||
196 | sx.sx_length != tip->i_d.di_size) { | ||
197 | error = XFS_ERROR(EFAULT); | ||
198 | goto error0; | ||
199 | } | ||
200 | |||
201 | /* | ||
202 | * If the target has extended attributes, the tmp file | ||
203 | * must also in order to ensure the correct data fork | ||
204 | * format. | ||
205 | */ | ||
206 | if ( XFS_IFORK_Q(ip) != XFS_IFORK_Q(tip) ) { | ||
207 | error = XFS_ERROR(EINVAL); | ||
208 | goto error0; | ||
209 | } | ||
210 | |||
211 | /* | ||
212 | * Compare the current change & modify times with that | ||
213 | * passed in. If they differ, we abort this swap. | ||
214 | * This is the mechanism used to ensure the calling | ||
215 | * process that the file was not changed out from | ||
216 | * under it. | ||
217 | */ | ||
218 | if ((sbp->bs_ctime.tv_sec != ip->i_d.di_ctime.t_sec) || | ||
219 | (sbp->bs_ctime.tv_nsec != ip->i_d.di_ctime.t_nsec) || | ||
220 | (sbp->bs_mtime.tv_sec != ip->i_d.di_mtime.t_sec) || | ||
221 | (sbp->bs_mtime.tv_nsec != ip->i_d.di_mtime.t_nsec)) { | ||
222 | error = XFS_ERROR(EBUSY); | ||
223 | goto error0; | ||
224 | } | ||
225 | |||
226 | /* We need to fail if the file is memory mapped. Once we have tossed | ||
227 | * all existing pages, the page fault will have no option | ||
228 | * but to go to the filesystem for pages. By making the page fault call | ||
229 | * VOP_READ (or write in the case of autogrow) they block on the iolock | ||
230 | * until we have switched the extents. | ||
231 | */ | ||
232 | if (VN_MAPPED(vp)) { | ||
233 | error = XFS_ERROR(EBUSY); | ||
234 | goto error0; | ||
235 | } | ||
236 | |||
237 | xfs_iunlock(ip, XFS_ILOCK_EXCL); | ||
238 | xfs_iunlock(tip, XFS_ILOCK_EXCL); | ||
239 | |||
240 | /* | ||
241 | * There is a race condition here since we gave up the | ||
242 | * ilock. However, the data fork will not change since | ||
243 | * we have the iolock (locked for truncation too) so we | ||
244 | * are safe. We don't really care if non-io related | ||
245 | * fields change. | ||
246 | */ | ||
247 | |||
248 | VOP_TOSS_PAGES(vp, 0, -1, FI_REMAPF); | ||
249 | |||
250 | tp = xfs_trans_alloc(mp, XFS_TRANS_SWAPEXT); | ||
251 | if ((error = xfs_trans_reserve(tp, 0, | ||
252 | XFS_ICHANGE_LOG_RES(mp), 0, | ||
253 | 0, 0))) { | ||
254 | xfs_iunlock(ip, XFS_IOLOCK_EXCL); | ||
255 | xfs_iunlock(tip, XFS_IOLOCK_EXCL); | ||
256 | xfs_trans_cancel(tp, 0); | ||
257 | return error; | ||
258 | } | ||
259 | xfs_lock_inodes(ips, 2, 0, XFS_ILOCK_EXCL); | ||
260 | |||
261 | /* | ||
262 | * Count the number of extended attribute blocks | ||
263 | */ | ||
264 | if ( ((XFS_IFORK_Q(ip) != 0) && (ip->i_d.di_anextents > 0)) && | ||
265 | (ip->i_d.di_aformat != XFS_DINODE_FMT_LOCAL)) { | ||
266 | error = xfs_bmap_count_blocks(tp, ip, XFS_ATTR_FORK, &aforkblks); | ||
267 | if (error) { | ||
268 | xfs_iunlock(ip, lock_flags); | ||
269 | xfs_iunlock(tip, lock_flags); | ||
270 | xfs_trans_cancel(tp, 0); | ||
271 | return error; | ||
272 | } | ||
273 | } | ||
274 | if ( ((XFS_IFORK_Q(tip) != 0) && (tip->i_d.di_anextents > 0)) && | ||
275 | (tip->i_d.di_aformat != XFS_DINODE_FMT_LOCAL)) { | ||
276 | error = xfs_bmap_count_blocks(tp, tip, XFS_ATTR_FORK, | ||
277 | &taforkblks); | ||
278 | if (error) { | ||
279 | xfs_iunlock(ip, lock_flags); | ||
280 | xfs_iunlock(tip, lock_flags); | ||
281 | xfs_trans_cancel(tp, 0); | ||
282 | return error; | ||
283 | } | ||
284 | } | ||
285 | |||
286 | /* | ||
287 | * Swap the data forks of the inodes | ||
288 | */ | ||
289 | ifp = &ip->i_df; | ||
290 | tifp = &tip->i_df; | ||
291 | tempif = *ifp; /* struct copy */ | ||
292 | *ifp = *tifp; /* struct copy */ | ||
293 | *tifp = tempif; /* struct copy */ | ||
294 | |||
295 | /* | ||
296 | * Fix the on-disk inode values | ||
297 | */ | ||
298 | tmp = (__uint64_t)ip->i_d.di_nblocks; | ||
299 | ip->i_d.di_nblocks = tip->i_d.di_nblocks - taforkblks + aforkblks; | ||
300 | tip->i_d.di_nblocks = tmp + taforkblks - aforkblks; | ||
301 | |||
302 | tmp = (__uint64_t) ip->i_d.di_nextents; | ||
303 | ip->i_d.di_nextents = tip->i_d.di_nextents; | ||
304 | tip->i_d.di_nextents = tmp; | ||
305 | |||
306 | tmp = (__uint64_t) ip->i_d.di_format; | ||
307 | ip->i_d.di_format = tip->i_d.di_format; | ||
308 | tip->i_d.di_format = tmp; | ||
309 | |||
310 | ilf_fields = XFS_ILOG_CORE; | ||
311 | |||
312 | switch(ip->i_d.di_format) { | ||
313 | case XFS_DINODE_FMT_EXTENTS: | ||
314 | /* If the extents fit in the inode, fix the | ||
315 | * pointer. Otherwise it's already NULL or | ||
316 | * pointing to the extent. | ||
317 | */ | ||
318 | if (ip->i_d.di_nextents <= XFS_INLINE_EXTS) { | ||
319 | ifp->if_u1.if_extents = | ||
320 | ifp->if_u2.if_inline_ext; | ||
321 | } | ||
322 | ilf_fields |= XFS_ILOG_DEXT; | ||
323 | break; | ||
324 | case XFS_DINODE_FMT_BTREE: | ||
325 | ilf_fields |= XFS_ILOG_DBROOT; | ||
326 | break; | ||
327 | } | ||
328 | |||
329 | tilf_fields = XFS_ILOG_CORE; | ||
330 | |||
331 | switch(tip->i_d.di_format) { | ||
332 | case XFS_DINODE_FMT_EXTENTS: | ||
333 | /* If the extents fit in the inode, fix the | ||
334 | * pointer. Otherwise it's already NULL or | ||
335 | * pointing to the extent. | ||
336 | */ | ||
337 | if (tip->i_d.di_nextents <= XFS_INLINE_EXTS) { | ||
338 | tifp->if_u1.if_extents = | ||
339 | tifp->if_u2.if_inline_ext; | ||
340 | } | ||
341 | tilf_fields |= XFS_ILOG_DEXT; | ||
342 | break; | ||
343 | case XFS_DINODE_FMT_BTREE: | ||
344 | tilf_fields |= XFS_ILOG_DBROOT; | ||
345 | break; | ||
346 | } | ||
347 | |||
348 | /* | ||
349 | * Increment vnode ref counts since xfs_trans_commit & | ||
350 | * xfs_trans_cancel will both unlock the inodes and | ||
351 | * decrement the associated ref counts. | ||
352 | */ | ||
353 | VN_HOLD(vp); | ||
354 | VN_HOLD(tvp); | ||
355 | |||
356 | xfs_trans_ijoin(tp, ip, lock_flags); | ||
357 | xfs_trans_ijoin(tp, tip, lock_flags); | ||
358 | |||
359 | xfs_trans_log_inode(tp, ip, ilf_fields); | ||
360 | xfs_trans_log_inode(tp, tip, tilf_fields); | ||
361 | |||
362 | /* | ||
363 | * If this is a synchronous mount, make sure that the | ||
364 | * transaction goes to disk before returning to the user. | ||
365 | */ | ||
366 | if (mp->m_flags & XFS_MOUNT_WSYNC) { | ||
367 | xfs_trans_set_sync(tp); | ||
368 | } | ||
369 | |||
370 | error = xfs_trans_commit(tp, XFS_TRANS_SWAPEXT, NULL); | ||
371 | |||
372 | fput(fp); | ||
373 | fput(tfp); | ||
374 | |||
375 | return error; | ||
376 | |||
377 | error0: | ||
378 | if (locked) { | ||
379 | xfs_iunlock(ip, lock_flags); | ||
380 | xfs_iunlock(tip, lock_flags); | ||
381 | } | ||
382 | |||
383 | if (fp != NULL) fput(fp); | ||
384 | if (tfp != NULL) fput(tfp); | ||
385 | |||
386 | return error; | ||
387 | } | ||