diff options
author | Dave Chinner <david@fromorbit.com> | 2014-07-14 17:37:18 -0400 |
---|---|---|
committer | Dave Chinner <david@fromorbit.com> | 2014-07-14 17:37:18 -0400 |
commit | 7f8a058f6dc52219117bc2469b1fb816f7fa1a4b (patch) | |
tree | 43ce8eed4d26beb6f2acff2279c43eae7f79f83a /fs/xfs/libxfs/xfs_dir2.c | |
parent | 03e01349c654fbdea80d3d9b4ab599244eb55bb7 (diff) | |
parent | 2451337dd043901b5270b7586942abe564443e3d (diff) |
Merge branch 'xfs-libxfs-restructure' into for-next
Diffstat (limited to 'fs/xfs/libxfs/xfs_dir2.c')
-rw-r--r-- | fs/xfs/libxfs/xfs_dir2.c | 762 |
1 files changed, 762 insertions, 0 deletions
diff --git a/fs/xfs/libxfs/xfs_dir2.c b/fs/xfs/libxfs/xfs_dir2.c new file mode 100644 index 000000000000..6cef22152fd6 --- /dev/null +++ b/fs/xfs/libxfs/xfs_dir2.c | |||
@@ -0,0 +1,762 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License as | ||
7 | * published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope that it would be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write the Free Software Foundation, | ||
16 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
17 | */ | ||
18 | #include "xfs.h" | ||
19 | #include "xfs_fs.h" | ||
20 | #include "xfs_format.h" | ||
21 | #include "xfs_log_format.h" | ||
22 | #include "xfs_trans_resv.h" | ||
23 | #include "xfs_inum.h" | ||
24 | #include "xfs_sb.h" | ||
25 | #include "xfs_ag.h" | ||
26 | #include "xfs_mount.h" | ||
27 | #include "xfs_da_format.h" | ||
28 | #include "xfs_da_btree.h" | ||
29 | #include "xfs_inode.h" | ||
30 | #include "xfs_trans.h" | ||
31 | #include "xfs_inode_item.h" | ||
32 | #include "xfs_bmap.h" | ||
33 | #include "xfs_dir2.h" | ||
34 | #include "xfs_dir2_priv.h" | ||
35 | #include "xfs_error.h" | ||
36 | #include "xfs_trace.h" | ||
37 | #include "xfs_dinode.h" | ||
38 | |||
39 | struct xfs_name xfs_name_dotdot = { (unsigned char *)"..", 2, XFS_DIR3_FT_DIR }; | ||
40 | |||
41 | |||
42 | /* | ||
43 | * ASCII case-insensitive (ie. A-Z) support for directories that was | ||
44 | * used in IRIX. | ||
45 | */ | ||
46 | STATIC xfs_dahash_t | ||
47 | xfs_ascii_ci_hashname( | ||
48 | struct xfs_name *name) | ||
49 | { | ||
50 | xfs_dahash_t hash; | ||
51 | int i; | ||
52 | |||
53 | for (i = 0, hash = 0; i < name->len; i++) | ||
54 | hash = tolower(name->name[i]) ^ rol32(hash, 7); | ||
55 | |||
56 | return hash; | ||
57 | } | ||
58 | |||
59 | STATIC enum xfs_dacmp | ||
60 | xfs_ascii_ci_compname( | ||
61 | struct xfs_da_args *args, | ||
62 | const unsigned char *name, | ||
63 | int len) | ||
64 | { | ||
65 | enum xfs_dacmp result; | ||
66 | int i; | ||
67 | |||
68 | if (args->namelen != len) | ||
69 | return XFS_CMP_DIFFERENT; | ||
70 | |||
71 | result = XFS_CMP_EXACT; | ||
72 | for (i = 0; i < len; i++) { | ||
73 | if (args->name[i] == name[i]) | ||
74 | continue; | ||
75 | if (tolower(args->name[i]) != tolower(name[i])) | ||
76 | return XFS_CMP_DIFFERENT; | ||
77 | result = XFS_CMP_CASE; | ||
78 | } | ||
79 | |||
80 | return result; | ||
81 | } | ||
82 | |||
83 | static struct xfs_nameops xfs_ascii_ci_nameops = { | ||
84 | .hashname = xfs_ascii_ci_hashname, | ||
85 | .compname = xfs_ascii_ci_compname, | ||
86 | }; | ||
87 | |||
88 | int | ||
89 | xfs_da_mount( | ||
90 | struct xfs_mount *mp) | ||
91 | { | ||
92 | struct xfs_da_geometry *dageo; | ||
93 | int nodehdr_size; | ||
94 | |||
95 | |||
96 | ASSERT(mp->m_sb.sb_versionnum & XFS_SB_VERSION_DIRV2BIT); | ||
97 | ASSERT((1 << (mp->m_sb.sb_blocklog + mp->m_sb.sb_dirblklog)) <= | ||
98 | XFS_MAX_BLOCKSIZE); | ||
99 | |||
100 | mp->m_dir_inode_ops = xfs_dir_get_ops(mp, NULL); | ||
101 | mp->m_nondir_inode_ops = xfs_nondir_get_ops(mp, NULL); | ||
102 | |||
103 | nodehdr_size = mp->m_dir_inode_ops->node_hdr_size; | ||
104 | mp->m_dir_geo = kmem_zalloc(sizeof(struct xfs_da_geometry), | ||
105 | KM_SLEEP | KM_MAYFAIL); | ||
106 | mp->m_attr_geo = kmem_zalloc(sizeof(struct xfs_da_geometry), | ||
107 | KM_SLEEP | KM_MAYFAIL); | ||
108 | if (!mp->m_dir_geo || !mp->m_attr_geo) { | ||
109 | kmem_free(mp->m_dir_geo); | ||
110 | kmem_free(mp->m_attr_geo); | ||
111 | return -ENOMEM; | ||
112 | } | ||
113 | |||
114 | /* set up directory geometry */ | ||
115 | dageo = mp->m_dir_geo; | ||
116 | dageo->blklog = mp->m_sb.sb_blocklog + mp->m_sb.sb_dirblklog; | ||
117 | dageo->fsblog = mp->m_sb.sb_blocklog; | ||
118 | dageo->blksize = 1 << dageo->blklog; | ||
119 | dageo->fsbcount = 1 << mp->m_sb.sb_dirblklog; | ||
120 | |||
121 | /* | ||
122 | * Now we've set up the block conversion variables, we can calculate the | ||
123 | * segment block constants using the geometry structure. | ||
124 | */ | ||
125 | dageo->datablk = xfs_dir2_byte_to_da(dageo, XFS_DIR2_DATA_OFFSET); | ||
126 | dageo->leafblk = xfs_dir2_byte_to_da(dageo, XFS_DIR2_LEAF_OFFSET); | ||
127 | dageo->freeblk = xfs_dir2_byte_to_da(dageo, XFS_DIR2_FREE_OFFSET); | ||
128 | dageo->node_ents = (dageo->blksize - nodehdr_size) / | ||
129 | (uint)sizeof(xfs_da_node_entry_t); | ||
130 | dageo->magicpct = (dageo->blksize * 37) / 100; | ||
131 | |||
132 | /* set up attribute geometry - single fsb only */ | ||
133 | dageo = mp->m_attr_geo; | ||
134 | dageo->blklog = mp->m_sb.sb_blocklog; | ||
135 | dageo->fsblog = mp->m_sb.sb_blocklog; | ||
136 | dageo->blksize = 1 << dageo->blklog; | ||
137 | dageo->fsbcount = 1; | ||
138 | dageo->node_ents = (dageo->blksize - nodehdr_size) / | ||
139 | (uint)sizeof(xfs_da_node_entry_t); | ||
140 | dageo->magicpct = (dageo->blksize * 37) / 100; | ||
141 | |||
142 | if (xfs_sb_version_hasasciici(&mp->m_sb)) | ||
143 | mp->m_dirnameops = &xfs_ascii_ci_nameops; | ||
144 | else | ||
145 | mp->m_dirnameops = &xfs_default_nameops; | ||
146 | |||
147 | return 0; | ||
148 | } | ||
149 | |||
150 | void | ||
151 | xfs_da_unmount( | ||
152 | struct xfs_mount *mp) | ||
153 | { | ||
154 | kmem_free(mp->m_dir_geo); | ||
155 | kmem_free(mp->m_attr_geo); | ||
156 | } | ||
157 | |||
158 | /* | ||
159 | * Return 1 if directory contains only "." and "..". | ||
160 | */ | ||
161 | int | ||
162 | xfs_dir_isempty( | ||
163 | xfs_inode_t *dp) | ||
164 | { | ||
165 | xfs_dir2_sf_hdr_t *sfp; | ||
166 | |||
167 | ASSERT(S_ISDIR(dp->i_d.di_mode)); | ||
168 | if (dp->i_d.di_size == 0) /* might happen during shutdown. */ | ||
169 | return 1; | ||
170 | if (dp->i_d.di_size > XFS_IFORK_DSIZE(dp)) | ||
171 | return 0; | ||
172 | sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; | ||
173 | return !sfp->count; | ||
174 | } | ||
175 | |||
176 | /* | ||
177 | * Validate a given inode number. | ||
178 | */ | ||
179 | int | ||
180 | xfs_dir_ino_validate( | ||
181 | xfs_mount_t *mp, | ||
182 | xfs_ino_t ino) | ||
183 | { | ||
184 | xfs_agblock_t agblkno; | ||
185 | xfs_agino_t agino; | ||
186 | xfs_agnumber_t agno; | ||
187 | int ino_ok; | ||
188 | int ioff; | ||
189 | |||
190 | agno = XFS_INO_TO_AGNO(mp, ino); | ||
191 | agblkno = XFS_INO_TO_AGBNO(mp, ino); | ||
192 | ioff = XFS_INO_TO_OFFSET(mp, ino); | ||
193 | agino = XFS_OFFBNO_TO_AGINO(mp, agblkno, ioff); | ||
194 | ino_ok = | ||
195 | agno < mp->m_sb.sb_agcount && | ||
196 | agblkno < mp->m_sb.sb_agblocks && | ||
197 | agblkno != 0 && | ||
198 | ioff < (1 << mp->m_sb.sb_inopblog) && | ||
199 | XFS_AGINO_TO_INO(mp, agno, agino) == ino; | ||
200 | if (unlikely(XFS_TEST_ERROR(!ino_ok, mp, XFS_ERRTAG_DIR_INO_VALIDATE, | ||
201 | XFS_RANDOM_DIR_INO_VALIDATE))) { | ||
202 | xfs_warn(mp, "Invalid inode number 0x%Lx", | ||
203 | (unsigned long long) ino); | ||
204 | XFS_ERROR_REPORT("xfs_dir_ino_validate", XFS_ERRLEVEL_LOW, mp); | ||
205 | return -EFSCORRUPTED; | ||
206 | } | ||
207 | return 0; | ||
208 | } | ||
209 | |||
210 | /* | ||
211 | * Initialize a directory with its "." and ".." entries. | ||
212 | */ | ||
213 | int | ||
214 | xfs_dir_init( | ||
215 | xfs_trans_t *tp, | ||
216 | xfs_inode_t *dp, | ||
217 | xfs_inode_t *pdp) | ||
218 | { | ||
219 | struct xfs_da_args *args; | ||
220 | int error; | ||
221 | |||
222 | ASSERT(S_ISDIR(dp->i_d.di_mode)); | ||
223 | error = xfs_dir_ino_validate(tp->t_mountp, pdp->i_ino); | ||
224 | if (error) | ||
225 | return error; | ||
226 | |||
227 | args = kmem_zalloc(sizeof(*args), KM_SLEEP | KM_NOFS); | ||
228 | if (!args) | ||
229 | return -ENOMEM; | ||
230 | |||
231 | args->geo = dp->i_mount->m_dir_geo; | ||
232 | args->dp = dp; | ||
233 | args->trans = tp; | ||
234 | error = xfs_dir2_sf_create(args, pdp->i_ino); | ||
235 | kmem_free(args); | ||
236 | return error; | ||
237 | } | ||
238 | |||
239 | /* | ||
240 | Enter a name in a directory. | ||
241 | */ | ||
242 | int | ||
243 | xfs_dir_createname( | ||
244 | xfs_trans_t *tp, | ||
245 | xfs_inode_t *dp, | ||
246 | struct xfs_name *name, | ||
247 | xfs_ino_t inum, /* new entry inode number */ | ||
248 | xfs_fsblock_t *first, /* bmap's firstblock */ | ||
249 | xfs_bmap_free_t *flist, /* bmap's freeblock list */ | ||
250 | xfs_extlen_t total) /* bmap's total block count */ | ||
251 | { | ||
252 | struct xfs_da_args *args; | ||
253 | int rval; | ||
254 | int v; /* type-checking value */ | ||
255 | |||
256 | ASSERT(S_ISDIR(dp->i_d.di_mode)); | ||
257 | rval = xfs_dir_ino_validate(tp->t_mountp, inum); | ||
258 | if (rval) | ||
259 | return rval; | ||
260 | XFS_STATS_INC(xs_dir_create); | ||
261 | |||
262 | args = kmem_zalloc(sizeof(*args), KM_SLEEP | KM_NOFS); | ||
263 | if (!args) | ||
264 | return -ENOMEM; | ||
265 | |||
266 | args->geo = dp->i_mount->m_dir_geo; | ||
267 | args->name = name->name; | ||
268 | args->namelen = name->len; | ||
269 | args->filetype = name->type; | ||
270 | args->hashval = dp->i_mount->m_dirnameops->hashname(name); | ||
271 | args->inumber = inum; | ||
272 | args->dp = dp; | ||
273 | args->firstblock = first; | ||
274 | args->flist = flist; | ||
275 | args->total = total; | ||
276 | args->whichfork = XFS_DATA_FORK; | ||
277 | args->trans = tp; | ||
278 | args->op_flags = XFS_DA_OP_ADDNAME | XFS_DA_OP_OKNOENT; | ||
279 | |||
280 | if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) { | ||
281 | rval = xfs_dir2_sf_addname(args); | ||
282 | goto out_free; | ||
283 | } | ||
284 | |||
285 | rval = xfs_dir2_isblock(args, &v); | ||
286 | if (rval) | ||
287 | goto out_free; | ||
288 | if (v) { | ||
289 | rval = xfs_dir2_block_addname(args); | ||
290 | goto out_free; | ||
291 | } | ||
292 | |||
293 | rval = xfs_dir2_isleaf(args, &v); | ||
294 | if (rval) | ||
295 | goto out_free; | ||
296 | if (v) | ||
297 | rval = xfs_dir2_leaf_addname(args); | ||
298 | else | ||
299 | rval = xfs_dir2_node_addname(args); | ||
300 | |||
301 | out_free: | ||
302 | kmem_free(args); | ||
303 | return rval; | ||
304 | } | ||
305 | |||
306 | /* | ||
307 | * If doing a CI lookup and case-insensitive match, dup actual name into | ||
308 | * args.value. Return EEXIST for success (ie. name found) or an error. | ||
309 | */ | ||
310 | int | ||
311 | xfs_dir_cilookup_result( | ||
312 | struct xfs_da_args *args, | ||
313 | const unsigned char *name, | ||
314 | int len) | ||
315 | { | ||
316 | if (args->cmpresult == XFS_CMP_DIFFERENT) | ||
317 | return -ENOENT; | ||
318 | if (args->cmpresult != XFS_CMP_CASE || | ||
319 | !(args->op_flags & XFS_DA_OP_CILOOKUP)) | ||
320 | return -EEXIST; | ||
321 | |||
322 | args->value = kmem_alloc(len, KM_NOFS | KM_MAYFAIL); | ||
323 | if (!args->value) | ||
324 | return -ENOMEM; | ||
325 | |||
326 | memcpy(args->value, name, len); | ||
327 | args->valuelen = len; | ||
328 | return -EEXIST; | ||
329 | } | ||
330 | |||
331 | /* | ||
332 | * Lookup a name in a directory, give back the inode number. | ||
333 | * If ci_name is not NULL, returns the actual name in ci_name if it differs | ||
334 | * to name, or ci_name->name is set to NULL for an exact match. | ||
335 | */ | ||
336 | |||
337 | int | ||
338 | xfs_dir_lookup( | ||
339 | xfs_trans_t *tp, | ||
340 | xfs_inode_t *dp, | ||
341 | struct xfs_name *name, | ||
342 | xfs_ino_t *inum, /* out: inode number */ | ||
343 | struct xfs_name *ci_name) /* out: actual name if CI match */ | ||
344 | { | ||
345 | struct xfs_da_args *args; | ||
346 | int rval; | ||
347 | int v; /* type-checking value */ | ||
348 | |||
349 | ASSERT(S_ISDIR(dp->i_d.di_mode)); | ||
350 | XFS_STATS_INC(xs_dir_lookup); | ||
351 | |||
352 | /* | ||
353 | * We need to use KM_NOFS here so that lockdep will not throw false | ||
354 | * positive deadlock warnings on a non-transactional lookup path. It is | ||
355 | * safe to recurse into inode recalim in that case, but lockdep can't | ||
356 | * easily be taught about it. Hence KM_NOFS avoids having to add more | ||
357 | * lockdep Doing this avoids having to add a bunch of lockdep class | ||
358 | * annotations into the reclaim path for the ilock. | ||
359 | */ | ||
360 | args = kmem_zalloc(sizeof(*args), KM_SLEEP | KM_NOFS); | ||
361 | args->geo = dp->i_mount->m_dir_geo; | ||
362 | args->name = name->name; | ||
363 | args->namelen = name->len; | ||
364 | args->filetype = name->type; | ||
365 | args->hashval = dp->i_mount->m_dirnameops->hashname(name); | ||
366 | args->dp = dp; | ||
367 | args->whichfork = XFS_DATA_FORK; | ||
368 | args->trans = tp; | ||
369 | args->op_flags = XFS_DA_OP_OKNOENT; | ||
370 | if (ci_name) | ||
371 | args->op_flags |= XFS_DA_OP_CILOOKUP; | ||
372 | |||
373 | if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) { | ||
374 | rval = xfs_dir2_sf_lookup(args); | ||
375 | goto out_check_rval; | ||
376 | } | ||
377 | |||
378 | rval = xfs_dir2_isblock(args, &v); | ||
379 | if (rval) | ||
380 | goto out_free; | ||
381 | if (v) { | ||
382 | rval = xfs_dir2_block_lookup(args); | ||
383 | goto out_check_rval; | ||
384 | } | ||
385 | |||
386 | rval = xfs_dir2_isleaf(args, &v); | ||
387 | if (rval) | ||
388 | goto out_free; | ||
389 | if (v) | ||
390 | rval = xfs_dir2_leaf_lookup(args); | ||
391 | else | ||
392 | rval = xfs_dir2_node_lookup(args); | ||
393 | |||
394 | out_check_rval: | ||
395 | if (rval == -EEXIST) | ||
396 | rval = 0; | ||
397 | if (!rval) { | ||
398 | *inum = args->inumber; | ||
399 | if (ci_name) { | ||
400 | ci_name->name = args->value; | ||
401 | ci_name->len = args->valuelen; | ||
402 | } | ||
403 | } | ||
404 | out_free: | ||
405 | kmem_free(args); | ||
406 | return rval; | ||
407 | } | ||
408 | |||
409 | /* | ||
410 | * Remove an entry from a directory. | ||
411 | */ | ||
412 | int | ||
413 | xfs_dir_removename( | ||
414 | xfs_trans_t *tp, | ||
415 | xfs_inode_t *dp, | ||
416 | struct xfs_name *name, | ||
417 | xfs_ino_t ino, | ||
418 | xfs_fsblock_t *first, /* bmap's firstblock */ | ||
419 | xfs_bmap_free_t *flist, /* bmap's freeblock list */ | ||
420 | xfs_extlen_t total) /* bmap's total block count */ | ||
421 | { | ||
422 | struct xfs_da_args *args; | ||
423 | int rval; | ||
424 | int v; /* type-checking value */ | ||
425 | |||
426 | ASSERT(S_ISDIR(dp->i_d.di_mode)); | ||
427 | XFS_STATS_INC(xs_dir_remove); | ||
428 | |||
429 | args = kmem_zalloc(sizeof(*args), KM_SLEEP | KM_NOFS); | ||
430 | if (!args) | ||
431 | return -ENOMEM; | ||
432 | |||
433 | args->geo = dp->i_mount->m_dir_geo; | ||
434 | args->name = name->name; | ||
435 | args->namelen = name->len; | ||
436 | args->filetype = name->type; | ||
437 | args->hashval = dp->i_mount->m_dirnameops->hashname(name); | ||
438 | args->inumber = ino; | ||
439 | args->dp = dp; | ||
440 | args->firstblock = first; | ||
441 | args->flist = flist; | ||
442 | args->total = total; | ||
443 | args->whichfork = XFS_DATA_FORK; | ||
444 | args->trans = tp; | ||
445 | |||
446 | if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) { | ||
447 | rval = xfs_dir2_sf_removename(args); | ||
448 | goto out_free; | ||
449 | } | ||
450 | |||
451 | rval = xfs_dir2_isblock(args, &v); | ||
452 | if (rval) | ||
453 | goto out_free; | ||
454 | if (v) { | ||
455 | rval = xfs_dir2_block_removename(args); | ||
456 | goto out_free; | ||
457 | } | ||
458 | |||
459 | rval = xfs_dir2_isleaf(args, &v); | ||
460 | if (rval) | ||
461 | goto out_free; | ||
462 | if (v) | ||
463 | rval = xfs_dir2_leaf_removename(args); | ||
464 | else | ||
465 | rval = xfs_dir2_node_removename(args); | ||
466 | out_free: | ||
467 | kmem_free(args); | ||
468 | return rval; | ||
469 | } | ||
470 | |||
471 | /* | ||
472 | * Replace the inode number of a directory entry. | ||
473 | */ | ||
474 | int | ||
475 | xfs_dir_replace( | ||
476 | xfs_trans_t *tp, | ||
477 | xfs_inode_t *dp, | ||
478 | struct xfs_name *name, /* name of entry to replace */ | ||
479 | xfs_ino_t inum, /* new inode number */ | ||
480 | xfs_fsblock_t *first, /* bmap's firstblock */ | ||
481 | xfs_bmap_free_t *flist, /* bmap's freeblock list */ | ||
482 | xfs_extlen_t total) /* bmap's total block count */ | ||
483 | { | ||
484 | struct xfs_da_args *args; | ||
485 | int rval; | ||
486 | int v; /* type-checking value */ | ||
487 | |||
488 | ASSERT(S_ISDIR(dp->i_d.di_mode)); | ||
489 | |||
490 | rval = xfs_dir_ino_validate(tp->t_mountp, inum); | ||
491 | if (rval) | ||
492 | return rval; | ||
493 | |||
494 | args = kmem_zalloc(sizeof(*args), KM_SLEEP | KM_NOFS); | ||
495 | if (!args) | ||
496 | return -ENOMEM; | ||
497 | |||
498 | args->geo = dp->i_mount->m_dir_geo; | ||
499 | args->name = name->name; | ||
500 | args->namelen = name->len; | ||
501 | args->filetype = name->type; | ||
502 | args->hashval = dp->i_mount->m_dirnameops->hashname(name); | ||
503 | args->inumber = inum; | ||
504 | args->dp = dp; | ||
505 | args->firstblock = first; | ||
506 | args->flist = flist; | ||
507 | args->total = total; | ||
508 | args->whichfork = XFS_DATA_FORK; | ||
509 | args->trans = tp; | ||
510 | |||
511 | if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) { | ||
512 | rval = xfs_dir2_sf_replace(args); | ||
513 | goto out_free; | ||
514 | } | ||
515 | |||
516 | rval = xfs_dir2_isblock(args, &v); | ||
517 | if (rval) | ||
518 | goto out_free; | ||
519 | if (v) { | ||
520 | rval = xfs_dir2_block_replace(args); | ||
521 | goto out_free; | ||
522 | } | ||
523 | |||
524 | rval = xfs_dir2_isleaf(args, &v); | ||
525 | if (rval) | ||
526 | goto out_free; | ||
527 | if (v) | ||
528 | rval = xfs_dir2_leaf_replace(args); | ||
529 | else | ||
530 | rval = xfs_dir2_node_replace(args); | ||
531 | out_free: | ||
532 | kmem_free(args); | ||
533 | return rval; | ||
534 | } | ||
535 | |||
536 | /* | ||
537 | * See if this entry can be added to the directory without allocating space. | ||
538 | * First checks that the caller couldn't reserve enough space (resblks = 0). | ||
539 | */ | ||
540 | int | ||
541 | xfs_dir_canenter( | ||
542 | xfs_trans_t *tp, | ||
543 | xfs_inode_t *dp, | ||
544 | struct xfs_name *name, /* name of entry to add */ | ||
545 | uint resblks) | ||
546 | { | ||
547 | struct xfs_da_args *args; | ||
548 | int rval; | ||
549 | int v; /* type-checking value */ | ||
550 | |||
551 | if (resblks) | ||
552 | return 0; | ||
553 | |||
554 | ASSERT(S_ISDIR(dp->i_d.di_mode)); | ||
555 | |||
556 | args = kmem_zalloc(sizeof(*args), KM_SLEEP | KM_NOFS); | ||
557 | if (!args) | ||
558 | return -ENOMEM; | ||
559 | |||
560 | args->geo = dp->i_mount->m_dir_geo; | ||
561 | args->name = name->name; | ||
562 | args->namelen = name->len; | ||
563 | args->filetype = name->type; | ||
564 | args->hashval = dp->i_mount->m_dirnameops->hashname(name); | ||
565 | args->dp = dp; | ||
566 | args->whichfork = XFS_DATA_FORK; | ||
567 | args->trans = tp; | ||
568 | args->op_flags = XFS_DA_OP_JUSTCHECK | XFS_DA_OP_ADDNAME | | ||
569 | XFS_DA_OP_OKNOENT; | ||
570 | |||
571 | if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) { | ||
572 | rval = xfs_dir2_sf_addname(args); | ||
573 | goto out_free; | ||
574 | } | ||
575 | |||
576 | rval = xfs_dir2_isblock(args, &v); | ||
577 | if (rval) | ||
578 | goto out_free; | ||
579 | if (v) { | ||
580 | rval = xfs_dir2_block_addname(args); | ||
581 | goto out_free; | ||
582 | } | ||
583 | |||
584 | rval = xfs_dir2_isleaf(args, &v); | ||
585 | if (rval) | ||
586 | goto out_free; | ||
587 | if (v) | ||
588 | rval = xfs_dir2_leaf_addname(args); | ||
589 | else | ||
590 | rval = xfs_dir2_node_addname(args); | ||
591 | out_free: | ||
592 | kmem_free(args); | ||
593 | return rval; | ||
594 | } | ||
595 | |||
596 | /* | ||
597 | * Utility routines. | ||
598 | */ | ||
599 | |||
600 | /* | ||
601 | * Add a block to the directory. | ||
602 | * | ||
603 | * This routine is for data and free blocks, not leaf/node blocks which are | ||
604 | * handled by xfs_da_grow_inode. | ||
605 | */ | ||
606 | int | ||
607 | xfs_dir2_grow_inode( | ||
608 | struct xfs_da_args *args, | ||
609 | int space, /* v2 dir's space XFS_DIR2_xxx_SPACE */ | ||
610 | xfs_dir2_db_t *dbp) /* out: block number added */ | ||
611 | { | ||
612 | struct xfs_inode *dp = args->dp; | ||
613 | struct xfs_mount *mp = dp->i_mount; | ||
614 | xfs_fileoff_t bno; /* directory offset of new block */ | ||
615 | int count; /* count of filesystem blocks */ | ||
616 | int error; | ||
617 | |||
618 | trace_xfs_dir2_grow_inode(args, space); | ||
619 | |||
620 | /* | ||
621 | * Set lowest possible block in the space requested. | ||
622 | */ | ||
623 | bno = XFS_B_TO_FSBT(mp, space * XFS_DIR2_SPACE_SIZE); | ||
624 | count = args->geo->fsbcount; | ||
625 | |||
626 | error = xfs_da_grow_inode_int(args, &bno, count); | ||
627 | if (error) | ||
628 | return error; | ||
629 | |||
630 | *dbp = xfs_dir2_da_to_db(args->geo, (xfs_dablk_t)bno); | ||
631 | |||
632 | /* | ||
633 | * Update file's size if this is the data space and it grew. | ||
634 | */ | ||
635 | if (space == XFS_DIR2_DATA_SPACE) { | ||
636 | xfs_fsize_t size; /* directory file (data) size */ | ||
637 | |||
638 | size = XFS_FSB_TO_B(mp, bno + count); | ||
639 | if (size > dp->i_d.di_size) { | ||
640 | dp->i_d.di_size = size; | ||
641 | xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE); | ||
642 | } | ||
643 | } | ||
644 | return 0; | ||
645 | } | ||
646 | |||
647 | /* | ||
648 | * See if the directory is a single-block form directory. | ||
649 | */ | ||
650 | int | ||
651 | xfs_dir2_isblock( | ||
652 | struct xfs_da_args *args, | ||
653 | int *vp) /* out: 1 is block, 0 is not block */ | ||
654 | { | ||
655 | xfs_fileoff_t last; /* last file offset */ | ||
656 | int rval; | ||
657 | |||
658 | if ((rval = xfs_bmap_last_offset(args->dp, &last, XFS_DATA_FORK))) | ||
659 | return rval; | ||
660 | rval = XFS_FSB_TO_B(args->dp->i_mount, last) == args->geo->blksize; | ||
661 | ASSERT(rval == 0 || args->dp->i_d.di_size == args->geo->blksize); | ||
662 | *vp = rval; | ||
663 | return 0; | ||
664 | } | ||
665 | |||
666 | /* | ||
667 | * See if the directory is a single-leaf form directory. | ||
668 | */ | ||
669 | int | ||
670 | xfs_dir2_isleaf( | ||
671 | struct xfs_da_args *args, | ||
672 | int *vp) /* out: 1 is block, 0 is not block */ | ||
673 | { | ||
674 | xfs_fileoff_t last; /* last file offset */ | ||
675 | int rval; | ||
676 | |||
677 | if ((rval = xfs_bmap_last_offset(args->dp, &last, XFS_DATA_FORK))) | ||
678 | return rval; | ||
679 | *vp = last == args->geo->leafblk + args->geo->fsbcount; | ||
680 | return 0; | ||
681 | } | ||
682 | |||
683 | /* | ||
684 | * Remove the given block from the directory. | ||
685 | * This routine is used for data and free blocks, leaf/node are done | ||
686 | * by xfs_da_shrink_inode. | ||
687 | */ | ||
688 | int | ||
689 | xfs_dir2_shrink_inode( | ||
690 | xfs_da_args_t *args, | ||
691 | xfs_dir2_db_t db, | ||
692 | struct xfs_buf *bp) | ||
693 | { | ||
694 | xfs_fileoff_t bno; /* directory file offset */ | ||
695 | xfs_dablk_t da; /* directory file offset */ | ||
696 | int done; /* bunmap is finished */ | ||
697 | xfs_inode_t *dp; | ||
698 | int error; | ||
699 | xfs_mount_t *mp; | ||
700 | xfs_trans_t *tp; | ||
701 | |||
702 | trace_xfs_dir2_shrink_inode(args, db); | ||
703 | |||
704 | dp = args->dp; | ||
705 | mp = dp->i_mount; | ||
706 | tp = args->trans; | ||
707 | da = xfs_dir2_db_to_da(args->geo, db); | ||
708 | /* | ||
709 | * Unmap the fsblock(s). | ||
710 | */ | ||
711 | if ((error = xfs_bunmapi(tp, dp, da, args->geo->fsbcount, | ||
712 | XFS_BMAPI_METADATA, 0, args->firstblock, args->flist, | ||
713 | &done))) { | ||
714 | /* | ||
715 | * ENOSPC actually can happen if we're in a removename with | ||
716 | * no space reservation, and the resulting block removal | ||
717 | * would cause a bmap btree split or conversion from extents | ||
718 | * to btree. This can only happen for un-fragmented | ||
719 | * directory blocks, since you need to be punching out | ||
720 | * the middle of an extent. | ||
721 | * In this case we need to leave the block in the file, | ||
722 | * and not binval it. | ||
723 | * So the block has to be in a consistent empty state | ||
724 | * and appropriately logged. | ||
725 | * We don't free up the buffer, the caller can tell it | ||
726 | * hasn't happened since it got an error back. | ||
727 | */ | ||
728 | return error; | ||
729 | } | ||
730 | ASSERT(done); | ||
731 | /* | ||
732 | * Invalidate the buffer from the transaction. | ||
733 | */ | ||
734 | xfs_trans_binval(tp, bp); | ||
735 | /* | ||
736 | * If it's not a data block, we're done. | ||
737 | */ | ||
738 | if (db >= xfs_dir2_byte_to_db(args->geo, XFS_DIR2_LEAF_OFFSET)) | ||
739 | return 0; | ||
740 | /* | ||
741 | * If the block isn't the last one in the directory, we're done. | ||
742 | */ | ||
743 | if (dp->i_d.di_size > xfs_dir2_db_off_to_byte(args->geo, db + 1, 0)) | ||
744 | return 0; | ||
745 | bno = da; | ||
746 | if ((error = xfs_bmap_last_before(tp, dp, &bno, XFS_DATA_FORK))) { | ||
747 | /* | ||
748 | * This can't really happen unless there's kernel corruption. | ||
749 | */ | ||
750 | return error; | ||
751 | } | ||
752 | if (db == args->geo->datablk) | ||
753 | ASSERT(bno == 0); | ||
754 | else | ||
755 | ASSERT(bno > 0); | ||
756 | /* | ||
757 | * Set the size to the new last block. | ||
758 | */ | ||
759 | dp->i_d.di_size = XFS_FSB_TO_B(mp, bno); | ||
760 | xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE); | ||
761 | return 0; | ||
762 | } | ||