]>
Commit | Line | Data |
---|---|---|
0b61f8a4 | 1 | // SPDX-License-Identifier: GPL-2.0 |
1da177e4 | 2 | /* |
7b718769 NS |
3 | * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc. |
4 | * All Rights Reserved. | |
1da177e4 | 5 | */ |
1da177e4 | 6 | #include "xfs.h" |
a844f451 | 7 | #include "xfs_fs.h" |
5467b34b | 8 | #include "xfs_shared.h" |
a4fbe6ab | 9 | #include "xfs_format.h" |
239880ef DC |
10 | #include "xfs_log_format.h" |
11 | #include "xfs_trans_resv.h" | |
1da177e4 | 12 | #include "xfs_mount.h" |
1da177e4 | 13 | #include "xfs_inode.h" |
239880ef | 14 | #include "xfs_trans.h" |
1da177e4 | 15 | #include "xfs_bmap.h" |
2b9ab5ab | 16 | #include "xfs_dir2.h" |
57926640 | 17 | #include "xfs_dir2_priv.h" |
e9e899a2 | 18 | #include "xfs_errortag.h" |
1da177e4 | 19 | #include "xfs_error.h" |
0b1b213f | 20 | #include "xfs_trace.h" |
1da177e4 | 21 | |
0cb97766 DC |
22 | struct xfs_name xfs_name_dotdot = { (unsigned char *)"..", 2, XFS_DIR3_FT_DIR }; |
23 | ||
1b767ee3 | 24 | /* |
1fc4d33f | 25 | * Convert inode mode to directory entry filetype |
1b767ee3 | 26 | */ |
a5c46e5e DW |
27 | unsigned char |
28 | xfs_mode_to_ftype( | |
29 | int mode) | |
1fc4d33f AG |
30 | { |
31 | switch (mode & S_IFMT) { | |
32 | case S_IFREG: | |
33 | return XFS_DIR3_FT_REG_FILE; | |
34 | case S_IFDIR: | |
35 | return XFS_DIR3_FT_DIR; | |
36 | case S_IFCHR: | |
37 | return XFS_DIR3_FT_CHRDEV; | |
38 | case S_IFBLK: | |
39 | return XFS_DIR3_FT_BLKDEV; | |
40 | case S_IFIFO: | |
41 | return XFS_DIR3_FT_FIFO; | |
42 | case S_IFSOCK: | |
43 | return XFS_DIR3_FT_SOCK; | |
44 | case S_IFLNK: | |
45 | return XFS_DIR3_FT_SYMLINK; | |
46 | default: | |
47 | return XFS_DIR3_FT_UNKNOWN; | |
48 | } | |
49 | } | |
1da177e4 | 50 | |
189f4bf2 BN |
51 | /* |
52 | * ASCII case-insensitive (ie. A-Z) support for directories that was | |
53 | * used in IRIX. | |
54 | */ | |
d8d11fc7 | 55 | xfs_dahash_t |
189f4bf2 BN |
56 | xfs_ascii_ci_hashname( |
57 | struct xfs_name *name) | |
58 | { | |
59 | xfs_dahash_t hash; | |
60 | int i; | |
61 | ||
62 | for (i = 0, hash = 0; i < name->len; i++) | |
63 | hash = tolower(name->name[i]) ^ rol32(hash, 7); | |
64 | ||
65 | return hash; | |
66 | } | |
67 | ||
d8d11fc7 | 68 | enum xfs_dacmp |
189f4bf2 | 69 | xfs_ascii_ci_compname( |
d8d11fc7 CH |
70 | struct xfs_da_args *args, |
71 | const unsigned char *name, | |
72 | int len) | |
189f4bf2 | 73 | { |
d8d11fc7 CH |
74 | enum xfs_dacmp result; |
75 | int i; | |
189f4bf2 BN |
76 | |
77 | if (args->namelen != len) | |
78 | return XFS_CMP_DIFFERENT; | |
79 | ||
80 | result = XFS_CMP_EXACT; | |
81 | for (i = 0; i < len; i++) { | |
82 | if (args->name[i] == name[i]) | |
83 | continue; | |
84 | if (tolower(args->name[i]) != tolower(name[i])) | |
85 | return XFS_CMP_DIFFERENT; | |
86 | result = XFS_CMP_CASE; | |
87 | } | |
88 | ||
89 | return result; | |
90 | } | |
91 | ||
0650b554 DC |
92 | int |
93 | xfs_da_mount( | |
94 | struct xfs_mount *mp) | |
1da177e4 | 95 | { |
0650b554 | 96 | struct xfs_da_geometry *dageo; |
37804376 DC |
97 | |
98 | ||
5d074a4f | 99 | ASSERT(mp->m_sb.sb_versionnum & XFS_SB_VERSION_DIRV2BIT); |
ac503a4c | 100 | ASSERT(xfs_dir2_dirblock_bytes(&mp->m_sb) <= XFS_MAX_BLOCKSIZE); |
4bceb18f | 101 | |
0650b554 | 102 | mp->m_dir_geo = kmem_zalloc(sizeof(struct xfs_da_geometry), |
707e0dda | 103 | KM_MAYFAIL); |
0650b554 | 104 | mp->m_attr_geo = kmem_zalloc(sizeof(struct xfs_da_geometry), |
707e0dda | 105 | KM_MAYFAIL); |
0650b554 DC |
106 | if (!mp->m_dir_geo || !mp->m_attr_geo) { |
107 | kmem_free(mp->m_dir_geo); | |
108 | kmem_free(mp->m_attr_geo); | |
2451337d | 109 | return -ENOMEM; |
0650b554 DC |
110 | } |
111 | ||
112 | /* set up directory geometry */ | |
113 | dageo = mp->m_dir_geo; | |
114 | dageo->blklog = mp->m_sb.sb_blocklog + mp->m_sb.sb_dirblklog; | |
115 | dageo->fsblog = mp->m_sb.sb_blocklog; | |
ac503a4c | 116 | dageo->blksize = xfs_dir2_dirblock_bytes(&mp->m_sb); |
0650b554 | 117 | dageo->fsbcount = 1 << mp->m_sb.sb_dirblklog; |
ebd9027d | 118 | if (xfs_has_crc(mp)) { |
3b344413 | 119 | dageo->node_hdr_size = sizeof(struct xfs_da3_node_hdr); |
545910bc | 120 | dageo->leaf_hdr_size = sizeof(struct xfs_dir3_leaf_hdr); |
ed1d612f | 121 | dageo->free_hdr_size = sizeof(struct xfs_dir3_free_hdr); |
d73e1cee CH |
122 | dageo->data_entry_offset = |
123 | sizeof(struct xfs_dir3_data_hdr); | |
545910bc | 124 | } else { |
3b344413 | 125 | dageo->node_hdr_size = sizeof(struct xfs_da_node_hdr); |
545910bc | 126 | dageo->leaf_hdr_size = sizeof(struct xfs_dir2_leaf_hdr); |
ed1d612f | 127 | dageo->free_hdr_size = sizeof(struct xfs_dir2_free_hdr); |
d73e1cee CH |
128 | dageo->data_entry_offset = |
129 | sizeof(struct xfs_dir2_data_hdr); | |
545910bc | 130 | } |
478c7835 CH |
131 | dageo->leaf_max_ents = (dageo->blksize - dageo->leaf_hdr_size) / |
132 | sizeof(struct xfs_dir2_leaf_entry); | |
5893e4fe CH |
133 | dageo->free_max_bests = (dageo->blksize - dageo->free_hdr_size) / |
134 | sizeof(xfs_dir2_data_off_t); | |
30028030 | 135 | |
d73e1cee CH |
136 | dageo->data_first_offset = dageo->data_entry_offset + |
137 | xfs_dir2_data_entsize(mp, 1) + | |
138 | xfs_dir2_data_entsize(mp, 2); | |
139 | ||
30028030 DC |
140 | /* |
141 | * Now we've set up the block conversion variables, we can calculate the | |
142 | * segment block constants using the geometry structure. | |
143 | */ | |
144 | dageo->datablk = xfs_dir2_byte_to_da(dageo, XFS_DIR2_DATA_OFFSET); | |
145 | dageo->leafblk = xfs_dir2_byte_to_da(dageo, XFS_DIR2_LEAF_OFFSET); | |
146 | dageo->freeblk = xfs_dir2_byte_to_da(dageo, XFS_DIR2_FREE_OFFSET); | |
3b344413 | 147 | dageo->node_ents = (dageo->blksize - dageo->node_hdr_size) / |
37804376 | 148 | (uint)sizeof(xfs_da_node_entry_t); |
0650b554 DC |
149 | dageo->magicpct = (dageo->blksize * 37) / 100; |
150 | ||
151 | /* set up attribute geometry - single fsb only */ | |
152 | dageo = mp->m_attr_geo; | |
153 | dageo->blklog = mp->m_sb.sb_blocklog; | |
154 | dageo->fsblog = mp->m_sb.sb_blocklog; | |
155 | dageo->blksize = 1 << dageo->blklog; | |
156 | dageo->fsbcount = 1; | |
3b344413 CH |
157 | dageo->node_hdr_size = mp->m_dir_geo->node_hdr_size; |
158 | dageo->node_ents = (dageo->blksize - dageo->node_hdr_size) / | |
37804376 | 159 | (uint)sizeof(xfs_da_node_entry_t); |
0650b554 | 160 | dageo->magicpct = (dageo->blksize * 37) / 100; |
0650b554 DC |
161 | return 0; |
162 | } | |
163 | ||
164 | void | |
165 | xfs_da_unmount( | |
166 | struct xfs_mount *mp) | |
167 | { | |
168 | kmem_free(mp->m_dir_geo); | |
169 | kmem_free(mp->m_attr_geo); | |
1da177e4 LT |
170 | } |
171 | ||
172 | /* | |
173 | * Return 1 if directory contains only "." and "..". | |
174 | */ | |
f6c2d1fa NS |
175 | int |
176 | xfs_dir_isempty( | |
177 | xfs_inode_t *dp) | |
1da177e4 | 178 | { |
ac8ba50f | 179 | xfs_dir2_sf_hdr_t *sfp; |
1da177e4 | 180 | |
c19b3b05 | 181 | ASSERT(S_ISDIR(VFS_I(dp)->i_mode)); |
13d2c10b | 182 | if (dp->i_disk_size == 0) /* might happen during shutdown. */ |
1da177e4 | 183 | return 1; |
13d2c10b | 184 | if (dp->i_disk_size > XFS_IFORK_DSIZE(dp)) |
1da177e4 | 185 | return 0; |
ac8ba50f CH |
186 | sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; |
187 | return !sfp->count; | |
1da177e4 LT |
188 | } |
189 | ||
f6c2d1fa NS |
190 | /* |
191 | * Validate a given inode number. | |
192 | */ | |
193 | int | |
194 | xfs_dir_ino_validate( | |
195 | xfs_mount_t *mp, | |
196 | xfs_ino_t ino) | |
197 | { | |
91fb9afc DW |
198 | bool ino_ok = xfs_verify_dir_ino(mp, ino); |
199 | ||
a71895c5 DW |
200 | if (XFS_IS_CORRUPT(mp, !ino_ok) || |
201 | XFS_TEST_ERROR(false, mp, XFS_ERRTAG_DIR_INO_VALIDATE)) { | |
53487786 | 202 | xfs_warn(mp, "Invalid inode number 0x%Lx", |
f6c2d1fa | 203 | (unsigned long long) ino); |
2451337d | 204 | return -EFSCORRUPTED; |
f6c2d1fa NS |
205 | } |
206 | return 0; | |
207 | } | |
208 | ||
1da177e4 LT |
209 | /* |
210 | * Initialize a directory with its "." and ".." entries. | |
211 | */ | |
f6c2d1fa NS |
212 | int |
213 | xfs_dir_init( | |
214 | xfs_trans_t *tp, | |
215 | xfs_inode_t *dp, | |
216 | xfs_inode_t *pdp) | |
1da177e4 | 217 | { |
a1358aa3 | 218 | struct xfs_da_args *args; |
f6c2d1fa | 219 | int error; |
1da177e4 | 220 | |
c19b3b05 | 221 | ASSERT(S_ISDIR(VFS_I(dp)->i_mode)); |
a1358aa3 DC |
222 | error = xfs_dir_ino_validate(tp->t_mountp, pdp->i_ino); |
223 | if (error) | |
1da177e4 | 224 | return error; |
a1358aa3 | 225 | |
707e0dda | 226 | args = kmem_zalloc(sizeof(*args), KM_NOFS); |
a1358aa3 | 227 | if (!args) |
2451337d | 228 | return -ENOMEM; |
a1358aa3 | 229 | |
0650b554 | 230 | args->geo = dp->i_mount->m_dir_geo; |
a1358aa3 DC |
231 | args->dp = dp; |
232 | args->trans = tp; | |
233 | error = xfs_dir2_sf_create(args, pdp->i_ino); | |
234 | kmem_free(args); | |
235 | return error; | |
1da177e4 LT |
236 | } |
237 | ||
238 | /* | |
b16ed7c1 ES |
239 | * Enter a name in a directory, or check for available space. |
240 | * If inum is 0, only the available space test is performed. | |
1da177e4 | 241 | */ |
f6c2d1fa NS |
242 | int |
243 | xfs_dir_createname( | |
32a9b7c6 BF |
244 | struct xfs_trans *tp, |
245 | struct xfs_inode *dp, | |
556b8b16 | 246 | struct xfs_name *name, |
1da177e4 | 247 | xfs_ino_t inum, /* new entry inode number */ |
1da177e4 LT |
248 | xfs_extlen_t total) /* bmap's total block count */ |
249 | { | |
a1358aa3 | 250 | struct xfs_da_args *args; |
f6c2d1fa | 251 | int rval; |
1da177e4 LT |
252 | int v; /* type-checking value */ |
253 | ||
c19b3b05 | 254 | ASSERT(S_ISDIR(VFS_I(dp)->i_mode)); |
c9cfdb38 | 255 | |
b16ed7c1 ES |
256 | if (inum) { |
257 | rval = xfs_dir_ino_validate(tp->t_mountp, inum); | |
258 | if (rval) | |
259 | return rval; | |
ff6d6af2 | 260 | XFS_STATS_INC(dp->i_mount, xs_dir_create); |
b16ed7c1 | 261 | } |
f6c2d1fa | 262 | |
707e0dda | 263 | args = kmem_zalloc(sizeof(*args), KM_NOFS); |
a1358aa3 | 264 | if (!args) |
2451337d | 265 | return -ENOMEM; |
a1358aa3 | 266 | |
0650b554 | 267 | args->geo = dp->i_mount->m_dir_geo; |
a1358aa3 DC |
268 | args->name = name->name; |
269 | args->namelen = name->len; | |
270 | args->filetype = name->type; | |
d8d11fc7 | 271 | args->hashval = xfs_dir2_hashname(dp->i_mount, name); |
a1358aa3 DC |
272 | args->inumber = inum; |
273 | args->dp = dp; | |
a1358aa3 DC |
274 | args->total = total; |
275 | args->whichfork = XFS_DATA_FORK; | |
276 | args->trans = tp; | |
277 | args->op_flags = XFS_DA_OP_ADDNAME | XFS_DA_OP_OKNOENT; | |
b16ed7c1 ES |
278 | if (!inum) |
279 | args->op_flags |= XFS_DA_OP_JUSTCHECK; | |
a1358aa3 | 280 | |
f7e67b20 | 281 | if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL) { |
a1358aa3 DC |
282 | rval = xfs_dir2_sf_addname(args); |
283 | goto out_free; | |
284 | } | |
285 | ||
53f82db0 | 286 | rval = xfs_dir2_isblock(args, &v); |
a1358aa3 DC |
287 | if (rval) |
288 | goto out_free; | |
289 | if (v) { | |
290 | rval = xfs_dir2_block_addname(args); | |
291 | goto out_free; | |
292 | } | |
293 | ||
53f82db0 | 294 | rval = xfs_dir2_isleaf(args, &v); |
a1358aa3 DC |
295 | if (rval) |
296 | goto out_free; | |
297 | if (v) | |
298 | rval = xfs_dir2_leaf_addname(args); | |
1da177e4 | 299 | else |
a1358aa3 DC |
300 | rval = xfs_dir2_node_addname(args); |
301 | ||
302 | out_free: | |
303 | kmem_free(args); | |
1da177e4 LT |
304 | return rval; |
305 | } | |
306 | ||
384f3ced BN |
307 | /* |
308 | * If doing a CI lookup and case-insensitive match, dup actual name into | |
309 | * args.value. Return EEXIST for success (ie. name found) or an error. | |
310 | */ | |
311 | int | |
312 | xfs_dir_cilookup_result( | |
313 | struct xfs_da_args *args, | |
a3380ae3 | 314 | const unsigned char *name, |
384f3ced BN |
315 | int len) |
316 | { | |
317 | if (args->cmpresult == XFS_CMP_DIFFERENT) | |
2451337d | 318 | return -ENOENT; |
384f3ced BN |
319 | if (args->cmpresult != XFS_CMP_CASE || |
320 | !(args->op_flags & XFS_DA_OP_CILOOKUP)) | |
2451337d | 321 | return -EEXIST; |
384f3ced | 322 | |
3f52c2f0 | 323 | args->value = kmem_alloc(len, KM_NOFS | KM_MAYFAIL); |
384f3ced | 324 | if (!args->value) |
2451337d | 325 | return -ENOMEM; |
384f3ced BN |
326 | |
327 | memcpy(args->value, name, len); | |
328 | args->valuelen = len; | |
2451337d | 329 | return -EEXIST; |
384f3ced BN |
330 | } |
331 | ||
1da177e4 LT |
332 | /* |
333 | * Lookup a name in a directory, give back the inode number. | |
384f3ced BN |
334 | * If ci_name is not NULL, returns the actual name in ci_name if it differs |
335 | * to name, or ci_name->name is set to NULL for an exact match. | |
1da177e4 | 336 | */ |
384f3ced | 337 | |
f6c2d1fa NS |
338 | int |
339 | xfs_dir_lookup( | |
340 | xfs_trans_t *tp, | |
341 | xfs_inode_t *dp, | |
556b8b16 | 342 | struct xfs_name *name, |
384f3ced BN |
343 | xfs_ino_t *inum, /* out: inode number */ |
344 | struct xfs_name *ci_name) /* out: actual name if CI match */ | |
1da177e4 | 345 | { |
a1358aa3 | 346 | struct xfs_da_args *args; |
f6c2d1fa | 347 | int rval; |
1da177e4 | 348 | int v; /* type-checking value */ |
dbad7c99 | 349 | int lock_mode; |
1da177e4 | 350 | |
c19b3b05 | 351 | ASSERT(S_ISDIR(VFS_I(dp)->i_mode)); |
ff6d6af2 | 352 | XFS_STATS_INC(dp->i_mount, xs_dir_lookup); |
1da177e4 | 353 | |
a1358aa3 DC |
354 | /* |
355 | * We need to use KM_NOFS here so that lockdep will not throw false | |
356 | * positive deadlock warnings on a non-transactional lookup path. It is | |
357 | * safe to recurse into inode recalim in that case, but lockdep can't | |
358 | * easily be taught about it. Hence KM_NOFS avoids having to add more | |
359 | * lockdep Doing this avoids having to add a bunch of lockdep class | |
360 | * annotations into the reclaim path for the ilock. | |
361 | */ | |
707e0dda | 362 | args = kmem_zalloc(sizeof(*args), KM_NOFS); |
0650b554 | 363 | args->geo = dp->i_mount->m_dir_geo; |
a1358aa3 DC |
364 | args->name = name->name; |
365 | args->namelen = name->len; | |
366 | args->filetype = name->type; | |
d8d11fc7 | 367 | args->hashval = xfs_dir2_hashname(dp->i_mount, name); |
a1358aa3 DC |
368 | args->dp = dp; |
369 | args->whichfork = XFS_DATA_FORK; | |
370 | args->trans = tp; | |
371 | args->op_flags = XFS_DA_OP_OKNOENT; | |
384f3ced | 372 | if (ci_name) |
a1358aa3 | 373 | args->op_flags |= XFS_DA_OP_CILOOKUP; |
f6c2d1fa | 374 | |
dbad7c99 | 375 | lock_mode = xfs_ilock_data_map_shared(dp); |
f7e67b20 | 376 | if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL) { |
a1358aa3 DC |
377 | rval = xfs_dir2_sf_lookup(args); |
378 | goto out_check_rval; | |
379 | } | |
380 | ||
53f82db0 | 381 | rval = xfs_dir2_isblock(args, &v); |
a1358aa3 DC |
382 | if (rval) |
383 | goto out_free; | |
384 | if (v) { | |
385 | rval = xfs_dir2_block_lookup(args); | |
386 | goto out_check_rval; | |
387 | } | |
388 | ||
53f82db0 | 389 | rval = xfs_dir2_isleaf(args, &v); |
a1358aa3 DC |
390 | if (rval) |
391 | goto out_free; | |
392 | if (v) | |
393 | rval = xfs_dir2_leaf_lookup(args); | |
1da177e4 | 394 | else |
a1358aa3 DC |
395 | rval = xfs_dir2_node_lookup(args); |
396 | ||
397 | out_check_rval: | |
2451337d | 398 | if (rval == -EEXIST) |
1da177e4 | 399 | rval = 0; |
384f3ced | 400 | if (!rval) { |
a1358aa3 | 401 | *inum = args->inumber; |
384f3ced | 402 | if (ci_name) { |
a1358aa3 DC |
403 | ci_name->name = args->value; |
404 | ci_name->len = args->valuelen; | |
384f3ced BN |
405 | } |
406 | } | |
a1358aa3 | 407 | out_free: |
dbad7c99 | 408 | xfs_iunlock(dp, lock_mode); |
a1358aa3 | 409 | kmem_free(args); |
1da177e4 LT |
410 | return rval; |
411 | } | |
412 | ||
413 | /* | |
414 | * Remove an entry from a directory. | |
415 | */ | |
f6c2d1fa NS |
416 | int |
417 | xfs_dir_removename( | |
32a9b7c6 BF |
418 | struct xfs_trans *tp, |
419 | struct xfs_inode *dp, | |
420 | struct xfs_name *name, | |
421 | xfs_ino_t ino, | |
32a9b7c6 | 422 | xfs_extlen_t total) /* bmap's total block count */ |
1da177e4 | 423 | { |
32a9b7c6 BF |
424 | struct xfs_da_args *args; |
425 | int rval; | |
426 | int v; /* type-checking value */ | |
1da177e4 | 427 | |
c19b3b05 | 428 | ASSERT(S_ISDIR(VFS_I(dp)->i_mode)); |
ff6d6af2 | 429 | XFS_STATS_INC(dp->i_mount, xs_dir_remove); |
f6c2d1fa | 430 | |
707e0dda | 431 | args = kmem_zalloc(sizeof(*args), KM_NOFS); |
a1358aa3 | 432 | if (!args) |
2451337d | 433 | return -ENOMEM; |
a1358aa3 | 434 | |
0650b554 | 435 | args->geo = dp->i_mount->m_dir_geo; |
a1358aa3 DC |
436 | args->name = name->name; |
437 | args->namelen = name->len; | |
438 | args->filetype = name->type; | |
d8d11fc7 | 439 | args->hashval = xfs_dir2_hashname(dp->i_mount, name); |
a1358aa3 DC |
440 | args->inumber = ino; |
441 | args->dp = dp; | |
a1358aa3 DC |
442 | args->total = total; |
443 | args->whichfork = XFS_DATA_FORK; | |
444 | args->trans = tp; | |
445 | ||
f7e67b20 | 446 | if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL) { |
a1358aa3 DC |
447 | rval = xfs_dir2_sf_removename(args); |
448 | goto out_free; | |
449 | } | |
450 | ||
53f82db0 | 451 | rval = xfs_dir2_isblock(args, &v); |
a1358aa3 DC |
452 | if (rval) |
453 | goto out_free; | |
454 | if (v) { | |
455 | rval = xfs_dir2_block_removename(args); | |
456 | goto out_free; | |
457 | } | |
458 | ||
53f82db0 | 459 | rval = xfs_dir2_isleaf(args, &v); |
a1358aa3 DC |
460 | if (rval) |
461 | goto out_free; | |
462 | if (v) | |
463 | rval = xfs_dir2_leaf_removename(args); | |
1da177e4 | 464 | else |
a1358aa3 DC |
465 | rval = xfs_dir2_node_removename(args); |
466 | out_free: | |
467 | kmem_free(args); | |
1da177e4 LT |
468 | return rval; |
469 | } | |
470 | ||
1da177e4 LT |
471 | /* |
472 | * Replace the inode number of a directory entry. | |
473 | */ | |
f6c2d1fa NS |
474 | int |
475 | xfs_dir_replace( | |
32a9b7c6 BF |
476 | struct xfs_trans *tp, |
477 | struct xfs_inode *dp, | |
478 | struct xfs_name *name, /* name of entry to replace */ | |
479 | xfs_ino_t inum, /* new inode number */ | |
32a9b7c6 | 480 | xfs_extlen_t total) /* bmap's total block count */ |
1da177e4 | 481 | { |
32a9b7c6 BF |
482 | struct xfs_da_args *args; |
483 | int rval; | |
484 | int v; /* type-checking value */ | |
1da177e4 | 485 | |
c19b3b05 | 486 | ASSERT(S_ISDIR(VFS_I(dp)->i_mode)); |
1da177e4 | 487 | |
a1358aa3 DC |
488 | rval = xfs_dir_ino_validate(tp->t_mountp, inum); |
489 | if (rval) | |
1da177e4 | 490 | return rval; |
f6c2d1fa | 491 | |
707e0dda | 492 | args = kmem_zalloc(sizeof(*args), KM_NOFS); |
a1358aa3 | 493 | if (!args) |
2451337d | 494 | return -ENOMEM; |
a1358aa3 | 495 | |
0650b554 | 496 | args->geo = dp->i_mount->m_dir_geo; |
a1358aa3 DC |
497 | args->name = name->name; |
498 | args->namelen = name->len; | |
499 | args->filetype = name->type; | |
d8d11fc7 | 500 | args->hashval = xfs_dir2_hashname(dp->i_mount, name); |
a1358aa3 DC |
501 | args->inumber = inum; |
502 | args->dp = dp; | |
a1358aa3 DC |
503 | args->total = total; |
504 | args->whichfork = XFS_DATA_FORK; | |
505 | args->trans = tp; | |
506 | ||
f7e67b20 | 507 | if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL) { |
a1358aa3 DC |
508 | rval = xfs_dir2_sf_replace(args); |
509 | goto out_free; | |
510 | } | |
511 | ||
53f82db0 | 512 | rval = xfs_dir2_isblock(args, &v); |
a1358aa3 DC |
513 | if (rval) |
514 | goto out_free; | |
515 | if (v) { | |
516 | rval = xfs_dir2_block_replace(args); | |
517 | goto out_free; | |
518 | } | |
519 | ||
53f82db0 | 520 | rval = xfs_dir2_isleaf(args, &v); |
a1358aa3 DC |
521 | if (rval) |
522 | goto out_free; | |
523 | if (v) | |
524 | rval = xfs_dir2_leaf_replace(args); | |
1da177e4 | 525 | else |
a1358aa3 DC |
526 | rval = xfs_dir2_node_replace(args); |
527 | out_free: | |
528 | kmem_free(args); | |
1da177e4 LT |
529 | return rval; |
530 | } | |
531 | ||
532 | /* | |
533 | * See if this entry can be added to the directory without allocating space. | |
534 | */ | |
f6c2d1fa NS |
535 | int |
536 | xfs_dir_canenter( | |
537 | xfs_trans_t *tp, | |
538 | xfs_inode_t *dp, | |
94f3cad5 | 539 | struct xfs_name *name) /* name of entry to add */ |
1da177e4 | 540 | { |
381eee69 | 541 | return xfs_dir_createname(tp, dp, name, 0, 0); |
1da177e4 LT |
542 | } |
543 | ||
1da177e4 LT |
544 | /* |
545 | * Utility routines. | |
546 | */ | |
547 | ||
548 | /* | |
549 | * Add a block to the directory. | |
77936d02 CH |
550 | * |
551 | * This routine is for data and free blocks, not leaf/node blocks which are | |
552 | * handled by xfs_da_grow_inode. | |
1da177e4 | 553 | */ |
f6c2d1fa | 554 | int |
1da177e4 | 555 | xfs_dir2_grow_inode( |
77936d02 CH |
556 | struct xfs_da_args *args, |
557 | int space, /* v2 dir's space XFS_DIR2_xxx_SPACE */ | |
558 | xfs_dir2_db_t *dbp) /* out: block number added */ | |
1da177e4 | 559 | { |
77936d02 CH |
560 | struct xfs_inode *dp = args->dp; |
561 | struct xfs_mount *mp = dp->i_mount; | |
562 | xfs_fileoff_t bno; /* directory offset of new block */ | |
563 | int count; /* count of filesystem blocks */ | |
564 | int error; | |
1da177e4 | 565 | |
0b1b213f CH |
566 | trace_xfs_dir2_grow_inode(args, space); |
567 | ||
1da177e4 LT |
568 | /* |
569 | * Set lowest possible block in the space requested. | |
570 | */ | |
571 | bno = XFS_B_TO_FSBT(mp, space * XFS_DIR2_SPACE_SIZE); | |
d6cf1305 | 572 | count = args->geo->fsbcount; |
77936d02 CH |
573 | |
574 | error = xfs_da_grow_inode_int(args, &bno, count); | |
575 | if (error) | |
1da177e4 | 576 | return error; |
a7444053 | 577 | |
2998ab1d | 578 | *dbp = xfs_dir2_da_to_db(args->geo, (xfs_dablk_t)bno); |
a7444053 | 579 | |
1da177e4 LT |
580 | /* |
581 | * Update file's size if this is the data space and it grew. | |
582 | */ | |
583 | if (space == XFS_DIR2_DATA_SPACE) { | |
584 | xfs_fsize_t size; /* directory file (data) size */ | |
585 | ||
586 | size = XFS_FSB_TO_B(mp, bno + count); | |
13d2c10b CH |
587 | if (size > dp->i_disk_size) { |
588 | dp->i_disk_size = size; | |
77936d02 | 589 | xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE); |
1da177e4 LT |
590 | } |
591 | } | |
592 | return 0; | |
593 | } | |
594 | ||
595 | /* | |
596 | * See if the directory is a single-block form directory. | |
597 | */ | |
f6c2d1fa | 598 | int |
1da177e4 | 599 | xfs_dir2_isblock( |
53f82db0 DC |
600 | struct xfs_da_args *args, |
601 | int *vp) /* out: 1 is block, 0 is not block */ | |
1da177e4 | 602 | { |
53f82db0 DC |
603 | xfs_fileoff_t last; /* last file offset */ |
604 | int rval; | |
1da177e4 | 605 | |
53f82db0 | 606 | if ((rval = xfs_bmap_last_offset(args->dp, &last, XFS_DATA_FORK))) |
1da177e4 | 607 | return rval; |
53f82db0 | 608 | rval = XFS_FSB_TO_B(args->dp->i_mount, last) == args->geo->blksize; |
a71895c5 DW |
609 | if (XFS_IS_CORRUPT(args->dp->i_mount, |
610 | rval != 0 && | |
13d2c10b | 611 | args->dp->i_disk_size != args->geo->blksize)) |
3c6f46ea | 612 | return -EFSCORRUPTED; |
1da177e4 LT |
613 | *vp = rval; |
614 | return 0; | |
615 | } | |
616 | ||
617 | /* | |
618 | * See if the directory is a single-leaf form directory. | |
619 | */ | |
f6c2d1fa | 620 | int |
1da177e4 | 621 | xfs_dir2_isleaf( |
53f82db0 DC |
622 | struct xfs_da_args *args, |
623 | int *vp) /* out: 1 is block, 0 is not block */ | |
1da177e4 | 624 | { |
53f82db0 DC |
625 | xfs_fileoff_t last; /* last file offset */ |
626 | int rval; | |
1da177e4 | 627 | |
53f82db0 | 628 | if ((rval = xfs_bmap_last_offset(args->dp, &last, XFS_DATA_FORK))) |
1da177e4 | 629 | return rval; |
53f82db0 | 630 | *vp = last == args->geo->leafblk + args->geo->fsbcount; |
1da177e4 LT |
631 | return 0; |
632 | } | |
633 | ||
1da177e4 LT |
634 | /* |
635 | * Remove the given block from the directory. | |
636 | * This routine is used for data and free blocks, leaf/node are done | |
637 | * by xfs_da_shrink_inode. | |
638 | */ | |
639 | int | |
640 | xfs_dir2_shrink_inode( | |
32a9b7c6 BF |
641 | struct xfs_da_args *args, |
642 | xfs_dir2_db_t db, | |
643 | struct xfs_buf *bp) | |
1da177e4 | 644 | { |
32a9b7c6 BF |
645 | xfs_fileoff_t bno; /* directory file offset */ |
646 | xfs_dablk_t da; /* directory file offset */ | |
647 | int done; /* bunmap is finished */ | |
648 | struct xfs_inode *dp; | |
649 | int error; | |
650 | struct xfs_mount *mp; | |
651 | struct xfs_trans *tp; | |
1da177e4 | 652 | |
0b1b213f CH |
653 | trace_xfs_dir2_shrink_inode(args, db); |
654 | ||
1da177e4 LT |
655 | dp = args->dp; |
656 | mp = dp->i_mount; | |
657 | tp = args->trans; | |
2998ab1d | 658 | da = xfs_dir2_db_to_da(args->geo, db); |
ab7bb610 DC |
659 | |
660 | /* Unmap the fsblock(s). */ | |
2af52842 | 661 | error = xfs_bunmapi(tp, dp, da, args->geo->fsbcount, 0, 0, &done); |
ab7bb610 | 662 | if (error) { |
1da177e4 | 663 | /* |
ab7bb610 DC |
664 | * ENOSPC actually can happen if we're in a removename with no |
665 | * space reservation, and the resulting block removal would | |
666 | * cause a bmap btree split or conversion from extents to btree. | |
667 | * This can only happen for un-fragmented directory blocks, | |
668 | * since you need to be punching out the middle of an extent. | |
669 | * In this case we need to leave the block in the file, and not | |
670 | * binval it. So the block has to be in a consistent empty | |
671 | * state and appropriately logged. We don't free up the buffer, | |
672 | * the caller can tell it hasn't happened since it got an error | |
673 | * back. | |
1da177e4 LT |
674 | */ |
675 | return error; | |
676 | } | |
677 | ASSERT(done); | |
678 | /* | |
679 | * Invalidate the buffer from the transaction. | |
680 | */ | |
1d9025e5 | 681 | xfs_trans_binval(tp, bp); |
1da177e4 LT |
682 | /* |
683 | * If it's not a data block, we're done. | |
684 | */ | |
30028030 | 685 | if (db >= xfs_dir2_byte_to_db(args->geo, XFS_DIR2_LEAF_OFFSET)) |
1da177e4 LT |
686 | return 0; |
687 | /* | |
688 | * If the block isn't the last one in the directory, we're done. | |
689 | */ | |
13d2c10b | 690 | if (dp->i_disk_size > xfs_dir2_db_off_to_byte(args->geo, db + 1, 0)) |
1da177e4 LT |
691 | return 0; |
692 | bno = da; | |
693 | if ((error = xfs_bmap_last_before(tp, dp, &bno, XFS_DATA_FORK))) { | |
694 | /* | |
695 | * This can't really happen unless there's kernel corruption. | |
696 | */ | |
697 | return error; | |
698 | } | |
7dda6e86 | 699 | if (db == args->geo->datablk) |
1da177e4 LT |
700 | ASSERT(bno == 0); |
701 | else | |
702 | ASSERT(bno > 0); | |
703 | /* | |
704 | * Set the size to the new last block. | |
705 | */ | |
13d2c10b | 706 | dp->i_disk_size = XFS_FSB_TO_B(mp, bno); |
1da177e4 LT |
707 | xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE); |
708 | return 0; | |
709 | } | |
e5d7d51b DW |
710 | |
711 | /* Returns true if the directory entry name is valid. */ | |
712 | bool | |
713 | xfs_dir2_namecheck( | |
714 | const void *name, | |
715 | size_t length) | |
716 | { | |
717 | /* | |
718 | * MAXNAMELEN includes the trailing null, but (name/length) leave it | |
719 | * out, so use >= for the length check. | |
720 | */ | |
721 | if (length >= MAXNAMELEN) | |
722 | return false; | |
723 | ||
724 | /* There shouldn't be any slashes or nulls here */ | |
725 | return !memchr(name, '/', length) && !memchr(name, 0, length); | |
726 | } | |
af952aeb DW |
727 | |
728 | xfs_dahash_t | |
729 | xfs_dir2_hashname( | |
730 | struct xfs_mount *mp, | |
731 | struct xfs_name *name) | |
732 | { | |
ebd9027d | 733 | if (unlikely(xfs_has_asciici(mp))) |
af952aeb DW |
734 | return xfs_ascii_ci_hashname(name); |
735 | return xfs_da_hashname(name->name, name->len); | |
736 | } | |
737 | ||
738 | enum xfs_dacmp | |
739 | xfs_dir2_compname( | |
740 | struct xfs_da_args *args, | |
741 | const unsigned char *name, | |
742 | int len) | |
743 | { | |
ebd9027d | 744 | if (unlikely(xfs_has_asciici(args->dp->i_mount))) |
af952aeb DW |
745 | return xfs_ascii_ci_compname(args, name, len); |
746 | return xfs_da_compname(args, name, len); | |
747 | } |