aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs/xfs_symlink.c
diff options
context:
space:
mode:
authorDave Chinner <dchinner@redhat.com>2013-04-03 01:11:18 -0400
committerBen Myers <bpm@sgi.com>2013-04-21 16:38:04 -0400
commit19de7351a8eb82dc99745e60e8f43474831d99c7 (patch)
tree2c0c1afc963c41e6766a08dfc631ccdfe913e574 /fs/xfs/xfs_symlink.c
parent93848a999cf9b9e4f4f77dba843a48c393f33c59 (diff)
xfs: split out symlink code into it's own file.
The symlink code is about to get more complicated when CRCs are added for remote symlink blocks. The symlink management code is mostly self contained, so move it to it's own files so that all the new code and the existing symlink code will not be intermingled with other unrelated code. Signed-off-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Ben Myers <bpm@sgi.com> Signed-off-by: Ben Myers <bpm@sgi.com>
Diffstat (limited to 'fs/xfs/xfs_symlink.c')
-rw-r--r--fs/xfs/xfs_symlink.c529
1 files changed, 529 insertions, 0 deletions
diff --git a/fs/xfs/xfs_symlink.c b/fs/xfs/xfs_symlink.c
new file mode 100644
index 000000000000..7512c96c7f29
--- /dev/null
+++ b/fs/xfs/xfs_symlink.c
@@ -0,0 +1,529 @@
1/*
2 * Copyright (c) 2000-2006 Silicon Graphics, Inc.
3 * Copyright (c) 2012-2013 Red Hat, Inc.
4 * All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it would be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * 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 the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19#include "xfs.h"
20#include "xfs_fs.h"
21#include "xfs_types.h"
22#include "xfs_bit.h"
23#include "xfs_log.h"
24#include "xfs_trans.h"
25#include "xfs_sb.h"
26#include "xfs_ag.h"
27#include "xfs_dir2.h"
28#include "xfs_mount.h"
29#include "xfs_da_btree.h"
30#include "xfs_bmap_btree.h"
31#include "xfs_ialloc_btree.h"
32#include "xfs_dinode.h"
33#include "xfs_inode.h"
34#include "xfs_inode_item.h"
35#include "xfs_itable.h"
36#include "xfs_ialloc.h"
37#include "xfs_alloc.h"
38#include "xfs_bmap.h"
39#include "xfs_error.h"
40#include "xfs_quota.h"
41#include "xfs_utils.h"
42#include "xfs_trans_space.h"
43#include "xfs_log_priv.h"
44#include "xfs_trace.h"
45#include "xfs_symlink.h"
46
47void
48xfs_symlink_local_to_remote(
49 struct xfs_trans *tp,
50 struct xfs_buf *bp,
51 struct xfs_inode *ip,
52 struct xfs_ifork *ifp)
53{
54 /* remote symlink blocks are not verifiable until CRCs come along */
55 bp->b_ops = NULL;
56 memcpy(bp->b_addr, ifp->if_u1.if_data, ifp->if_bytes);
57}
58
59/* ----- Kernel only functions below ----- */
60
61STATIC int
62xfs_readlink_bmap(
63 xfs_inode_t *ip,
64 char *link)
65{
66 xfs_mount_t *mp = ip->i_mount;
67 int pathlen = ip->i_d.di_size;
68 int nmaps = XFS_SYMLINK_MAPS;
69 xfs_bmbt_irec_t mval[XFS_SYMLINK_MAPS];
70 xfs_daddr_t d;
71 int byte_cnt;
72 int n;
73 xfs_buf_t *bp;
74 int error = 0;
75
76 error = xfs_bmapi_read(ip, 0, XFS_B_TO_FSB(mp, pathlen), mval, &nmaps,
77 0);
78 if (error)
79 goto out;
80
81 for (n = 0; n < nmaps; n++) {
82 d = XFS_FSB_TO_DADDR(mp, mval[n].br_startblock);
83 byte_cnt = XFS_FSB_TO_B(mp, mval[n].br_blockcount);
84
85 bp = xfs_buf_read(mp->m_ddev_targp, d, BTOBB(byte_cnt), 0, NULL);
86 if (!bp)
87 return XFS_ERROR(ENOMEM);
88 error = bp->b_error;
89 if (error) {
90 xfs_buf_ioerror_alert(bp, __func__);
91 xfs_buf_relse(bp);
92 goto out;
93 }
94 if (pathlen < byte_cnt)
95 byte_cnt = pathlen;
96 pathlen -= byte_cnt;
97
98 memcpy(link, bp->b_addr, byte_cnt);
99 xfs_buf_relse(bp);
100 }
101
102 link[ip->i_d.di_size] = '\0';
103 error = 0;
104
105 out:
106 return error;
107}
108
109int
110xfs_readlink(
111 xfs_inode_t *ip,
112 char *link)
113{
114 xfs_mount_t *mp = ip->i_mount;
115 xfs_fsize_t pathlen;
116 int error = 0;
117
118 trace_xfs_readlink(ip);
119
120 if (XFS_FORCED_SHUTDOWN(mp))
121 return XFS_ERROR(EIO);
122
123 xfs_ilock(ip, XFS_ILOCK_SHARED);
124
125 pathlen = ip->i_d.di_size;
126 if (!pathlen)
127 goto out;
128
129 if (pathlen < 0 || pathlen > MAXPATHLEN) {
130 xfs_alert(mp, "%s: inode (%llu) bad symlink length (%lld)",
131 __func__, (unsigned long long) ip->i_ino,
132 (long long) pathlen);
133 ASSERT(0);
134 error = XFS_ERROR(EFSCORRUPTED);
135 goto out;
136 }
137
138
139 if (ip->i_df.if_flags & XFS_IFINLINE) {
140 memcpy(link, ip->i_df.if_u1.if_data, pathlen);
141 link[pathlen] = '\0';
142 } else {
143 error = xfs_readlink_bmap(ip, link);
144 }
145
146 out:
147 xfs_iunlock(ip, XFS_ILOCK_SHARED);
148 return error;
149}
150
151int
152xfs_symlink(
153 xfs_inode_t *dp,
154 struct xfs_name *link_name,
155 const char *target_path,
156 umode_t mode,
157 xfs_inode_t **ipp)
158{
159 xfs_mount_t *mp = dp->i_mount;
160 xfs_trans_t *tp;
161 xfs_inode_t *ip;
162 int error;
163 int pathlen;
164 xfs_bmap_free_t free_list;
165 xfs_fsblock_t first_block;
166 bool unlock_dp_on_error = false;
167 uint cancel_flags;
168 int committed;
169 xfs_fileoff_t first_fsb;
170 xfs_filblks_t fs_blocks;
171 int nmaps;
172 xfs_bmbt_irec_t mval[XFS_SYMLINK_MAPS];
173 xfs_daddr_t d;
174 const char *cur_chunk;
175 int byte_cnt;
176 int n;
177 xfs_buf_t *bp;
178 prid_t prid;
179 struct xfs_dquot *udqp, *gdqp;
180 uint resblks;
181
182 *ipp = NULL;
183 error = 0;
184 ip = NULL;
185 tp = NULL;
186
187 trace_xfs_symlink(dp, link_name);
188
189 if (XFS_FORCED_SHUTDOWN(mp))
190 return XFS_ERROR(EIO);
191
192 /*
193 * Check component lengths of the target path name.
194 */
195 pathlen = strlen(target_path);
196 if (pathlen >= MAXPATHLEN) /* total string too long */
197 return XFS_ERROR(ENAMETOOLONG);
198
199 udqp = gdqp = NULL;
200 if (dp->i_d.di_flags & XFS_DIFLAG_PROJINHERIT)
201 prid = xfs_get_projid(dp);
202 else
203 prid = XFS_PROJID_DEFAULT;
204
205 /*
206 * Make sure that we have allocated dquot(s) on disk.
207 */
208 error = xfs_qm_vop_dqalloc(dp, current_fsuid(), current_fsgid(), prid,
209 XFS_QMOPT_QUOTALL | XFS_QMOPT_INHERIT, &udqp, &gdqp);
210 if (error)
211 goto std_return;
212
213 tp = xfs_trans_alloc(mp, XFS_TRANS_SYMLINK);
214 cancel_flags = XFS_TRANS_RELEASE_LOG_RES;
215 /*
216 * The symlink will fit into the inode data fork?
217 * There can't be any attributes so we get the whole variable part.
218 */
219 if (pathlen <= XFS_LITINO(mp, dp->i_d.di_version))
220 fs_blocks = 0;
221 else
222 fs_blocks = XFS_B_TO_FSB(mp, pathlen);
223 resblks = XFS_SYMLINK_SPACE_RES(mp, link_name->len, fs_blocks);
224 error = xfs_trans_reserve(tp, resblks, XFS_SYMLINK_LOG_RES(mp), 0,
225 XFS_TRANS_PERM_LOG_RES, XFS_SYMLINK_LOG_COUNT);
226 if (error == ENOSPC && fs_blocks == 0) {
227 resblks = 0;
228 error = xfs_trans_reserve(tp, 0, XFS_SYMLINK_LOG_RES(mp), 0,
229 XFS_TRANS_PERM_LOG_RES, XFS_SYMLINK_LOG_COUNT);
230 }
231 if (error) {
232 cancel_flags = 0;
233 goto error_return;
234 }
235
236 xfs_ilock(dp, XFS_ILOCK_EXCL | XFS_ILOCK_PARENT);
237 unlock_dp_on_error = true;
238
239 /*
240 * Check whether the directory allows new symlinks or not.
241 */
242 if (dp->i_d.di_flags & XFS_DIFLAG_NOSYMLINKS) {
243 error = XFS_ERROR(EPERM);
244 goto error_return;
245 }
246
247 /*
248 * Reserve disk quota : blocks and inode.
249 */
250 error = xfs_trans_reserve_quota(tp, mp, udqp, gdqp, resblks, 1, 0);
251 if (error)
252 goto error_return;
253
254 /*
255 * Check for ability to enter directory entry, if no space reserved.
256 */
257 error = xfs_dir_canenter(tp, dp, link_name, resblks);
258 if (error)
259 goto error_return;
260 /*
261 * Initialize the bmap freelist prior to calling either
262 * bmapi or the directory create code.
263 */
264 xfs_bmap_init(&free_list, &first_block);
265
266 /*
267 * Allocate an inode for the symlink.
268 */
269 error = xfs_dir_ialloc(&tp, dp, S_IFLNK | (mode & ~S_IFMT), 1, 0,
270 prid, resblks > 0, &ip, NULL);
271 if (error) {
272 if (error == ENOSPC)
273 goto error_return;
274 goto error1;
275 }
276
277 /*
278 * An error after we've joined dp to the transaction will result in the
279 * transaction cancel unlocking dp so don't do it explicitly in the
280 * error path.
281 */
282 xfs_trans_ijoin(tp, dp, XFS_ILOCK_EXCL);
283 unlock_dp_on_error = false;
284
285 /*
286 * Also attach the dquot(s) to it, if applicable.
287 */
288 xfs_qm_vop_create_dqattach(tp, ip, udqp, gdqp);
289
290 if (resblks)
291 resblks -= XFS_IALLOC_SPACE_RES(mp);
292 /*
293 * If the symlink will fit into the inode, write it inline.
294 */
295 if (pathlen <= XFS_IFORK_DSIZE(ip)) {
296 xfs_idata_realloc(ip, pathlen, XFS_DATA_FORK);
297 memcpy(ip->i_df.if_u1.if_data, target_path, pathlen);
298 ip->i_d.di_size = pathlen;
299
300 /*
301 * The inode was initially created in extent format.
302 */
303 ip->i_df.if_flags &= ~(XFS_IFEXTENTS | XFS_IFBROOT);
304 ip->i_df.if_flags |= XFS_IFINLINE;
305
306 ip->i_d.di_format = XFS_DINODE_FMT_LOCAL;
307 xfs_trans_log_inode(tp, ip, XFS_ILOG_DDATA | XFS_ILOG_CORE);
308
309 } else {
310 first_fsb = 0;
311 nmaps = XFS_SYMLINK_MAPS;
312
313 error = xfs_bmapi_write(tp, ip, first_fsb, fs_blocks,
314 XFS_BMAPI_METADATA, &first_block, resblks,
315 mval, &nmaps, &free_list);
316 if (error)
317 goto error2;
318
319 if (resblks)
320 resblks -= fs_blocks;
321 ip->i_d.di_size = pathlen;
322 xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
323
324 cur_chunk = target_path;
325 for (n = 0; n < nmaps; n++) {
326 d = XFS_FSB_TO_DADDR(mp, mval[n].br_startblock);
327 byte_cnt = XFS_FSB_TO_B(mp, mval[n].br_blockcount);
328 bp = xfs_trans_get_buf(tp, mp->m_ddev_targp, d,
329 BTOBB(byte_cnt), 0);
330 if (!bp) {
331 error = ENOMEM;
332 goto error2;
333 }
334 if (pathlen < byte_cnt) {
335 byte_cnt = pathlen;
336 }
337 pathlen -= byte_cnt;
338
339 memcpy(bp->b_addr, cur_chunk, byte_cnt);
340 cur_chunk += byte_cnt;
341
342 xfs_trans_log_buf(tp, bp, 0, byte_cnt - 1);
343 }
344 }
345
346 /*
347 * Create the directory entry for the symlink.
348 */
349 error = xfs_dir_createname(tp, dp, link_name, ip->i_ino,
350 &first_block, &free_list, resblks);
351 if (error)
352 goto error2;
353 xfs_trans_ichgtime(tp, dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
354 xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);
355
356 /*
357 * If this is a synchronous mount, make sure that the
358 * symlink transaction goes to disk before returning to
359 * the user.
360 */
361 if (mp->m_flags & (XFS_MOUNT_WSYNC|XFS_MOUNT_DIRSYNC)) {
362 xfs_trans_set_sync(tp);
363 }
364
365 error = xfs_bmap_finish(&tp, &free_list, &committed);
366 if (error) {
367 goto error2;
368 }
369 error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES);
370 xfs_qm_dqrele(udqp);
371 xfs_qm_dqrele(gdqp);
372
373 *ipp = ip;
374 return 0;
375
376 error2:
377 IRELE(ip);
378 error1:
379 xfs_bmap_cancel(&free_list);
380 cancel_flags |= XFS_TRANS_ABORT;
381 error_return:
382 xfs_trans_cancel(tp, cancel_flags);
383 xfs_qm_dqrele(udqp);
384 xfs_qm_dqrele(gdqp);
385
386 if (unlock_dp_on_error)
387 xfs_iunlock(dp, XFS_ILOCK_EXCL);
388 std_return:
389 return error;
390}
391
392/*
393 * Free a symlink that has blocks associated with it.
394 */
395STATIC int
396xfs_inactive_symlink_rmt(
397 xfs_inode_t *ip,
398 xfs_trans_t **tpp)
399{
400 xfs_buf_t *bp;
401 int committed;
402 int done;
403 int error;
404 xfs_fsblock_t first_block;
405 xfs_bmap_free_t free_list;
406 int i;
407 xfs_mount_t *mp;
408 xfs_bmbt_irec_t mval[XFS_SYMLINK_MAPS];
409 int nmaps;
410 xfs_trans_t *ntp;
411 int size;
412 xfs_trans_t *tp;
413
414 tp = *tpp;
415 mp = ip->i_mount;
416 ASSERT(ip->i_d.di_size > XFS_IFORK_DSIZE(ip));
417 /*
418 * We're freeing a symlink that has some
419 * blocks allocated to it. Free the
420 * blocks here. We know that we've got
421 * either 1 or 2 extents and that we can
422 * free them all in one bunmapi call.
423 */
424 ASSERT(ip->i_d.di_nextents > 0 && ip->i_d.di_nextents <= 2);
425
426 /*
427 * Lock the inode, fix the size, and join it to the transaction.
428 * Hold it so in the normal path, we still have it locked for
429 * the second transaction. In the error paths we need it
430 * held so the cancel won't rele it, see below.
431 */
432 size = (int)ip->i_d.di_size;
433 ip->i_d.di_size = 0;
434 xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
435 /*
436 * Find the block(s) so we can inval and unmap them.
437 */
438 done = 0;
439 xfs_bmap_init(&free_list, &first_block);
440 nmaps = ARRAY_SIZE(mval);
441 error = xfs_bmapi_read(ip, 0, XFS_B_TO_FSB(mp, size),
442 mval, &nmaps, 0);
443 if (error)
444 goto error0;
445 /*
446 * Invalidate the block(s).
447 */
448 for (i = 0; i < nmaps; i++) {
449 bp = xfs_trans_get_buf(tp, mp->m_ddev_targp,
450 XFS_FSB_TO_DADDR(mp, mval[i].br_startblock),
451 XFS_FSB_TO_BB(mp, mval[i].br_blockcount), 0);
452 if (!bp) {
453 error = ENOMEM;
454 goto error1;
455 }
456 xfs_trans_binval(tp, bp);
457 }
458 /*
459 * Unmap the dead block(s) to the free_list.
460 */
461 if ((error = xfs_bunmapi(tp, ip, 0, size, XFS_BMAPI_METADATA, nmaps,
462 &first_block, &free_list, &done)))
463 goto error1;
464 ASSERT(done);
465 /*
466 * Commit the first transaction. This logs the EFI and the inode.
467 */
468 if ((error = xfs_bmap_finish(&tp, &free_list, &committed)))
469 goto error1;
470 /*
471 * The transaction must have been committed, since there were
472 * actually extents freed by xfs_bunmapi. See xfs_bmap_finish.
473 * The new tp has the extent freeing and EFDs.
474 */
475 ASSERT(committed);
476 /*
477 * The first xact was committed, so add the inode to the new one.
478 * Mark it dirty so it will be logged and moved forward in the log as
479 * part of every commit.
480 */
481 xfs_trans_ijoin(tp, ip, 0);
482 xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
483 /*
484 * Get a new, empty transaction to return to our caller.
485 */
486 ntp = xfs_trans_dup(tp);
487 /*
488 * Commit the transaction containing extent freeing and EFDs.
489 * If we get an error on the commit here or on the reserve below,
490 * we need to unlock the inode since the new transaction doesn't
491 * have the inode attached.
492 */
493 error = xfs_trans_commit(tp, 0);
494 tp = ntp;
495 if (error) {
496 ASSERT(XFS_FORCED_SHUTDOWN(mp));
497 goto error0;
498 }
499 /*
500 * transaction commit worked ok so we can drop the extra ticket
501 * reference that we gained in xfs_trans_dup()
502 */
503 xfs_log_ticket_put(tp->t_ticket);
504
505 /*
506 * Remove the memory for extent descriptions (just bookkeeping).
507 */
508 if (ip->i_df.if_bytes)
509 xfs_idata_realloc(ip, -ip->i_df.if_bytes, XFS_DATA_FORK);
510 ASSERT(ip->i_df.if_bytes == 0);
511 /*
512 * Put an itruncate log reservation in the new transaction
513 * for our caller.
514 */
515 if ((error = xfs_trans_reserve(tp, 0, XFS_ITRUNCATE_LOG_RES(mp), 0,
516 XFS_TRANS_PERM_LOG_RES, XFS_ITRUNCATE_LOG_COUNT))) {
517 ASSERT(XFS_FORCED_SHUTDOWN(mp));
518 goto error0;
519 }
520
521 xfs_trans_ijoin(tp, ip, 0);
522 *tpp = tp;
523 return 0;
524
525 error1:
526 xfs_bmap_cancel(&free_list);
527 error0:
528 return error;
529}