]>
Commit | Line | Data |
---|---|---|
2bd0ea18 | 1 | /* |
5e656dbb | 2 | * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc. |
da23017d | 3 | * All Rights Reserved. |
5000d01d | 4 | * |
da23017d NS |
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 | |
2bd0ea18 | 7 | * published by the Free Software Foundation. |
5000d01d | 8 | * |
da23017d NS |
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. | |
5000d01d | 13 | * |
da23017d NS |
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 | |
2bd0ea18 | 17 | */ |
9c799827 | 18 | #include "libxfs_priv.h" |
b626fb59 DC |
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_mount.h" | |
24 | #include "xfs_da_format.h" | |
25 | #include "xfs_da_btree.h" | |
26 | #include "xfs_inode.h" | |
27 | #include "xfs_trans.h" | |
28 | #include "xfs_dir2.h" | |
29 | #include "xfs_dir2_priv.h" | |
30 | #include "xfs_trace.h" | |
2bd0ea18 | 31 | |
5e656dbb BN |
32 | /* |
33 | * Prototypes for internal functions. | |
34 | */ | |
35 | static void xfs_dir2_sf_addname_easy(xfs_da_args_t *args, | |
36 | xfs_dir2_sf_entry_t *sfep, | |
37 | xfs_dir2_data_aoff_t offset, | |
38 | int new_isize); | |
39 | static void xfs_dir2_sf_addname_hard(xfs_da_args_t *args, int objchange, | |
40 | int new_isize); | |
41 | static int xfs_dir2_sf_addname_pick(xfs_da_args_t *args, int objchange, | |
42 | xfs_dir2_sf_entry_t **sfepp, | |
43 | xfs_dir2_data_aoff_t *offsetp); | |
44 | #ifdef DEBUG | |
45 | static void xfs_dir2_sf_check(xfs_da_args_t *args); | |
46 | #else | |
47 | #define xfs_dir2_sf_check(args) | |
48 | #endif /* DEBUG */ | |
5a35bf2c | 49 | |
5e656dbb BN |
50 | static void xfs_dir2_sf_toino4(xfs_da_args_t *args); |
51 | static void xfs_dir2_sf_toino8(xfs_da_args_t *args); | |
2bd0ea18 NS |
52 | |
53 | /* | |
54 | * Given a block directory (dp/block), calculate its size as a shortform (sf) | |
55 | * directory and a header for the sf directory, if it will fit it the | |
56 | * space currently present in the inode. If it won't fit, the output | |
57 | * size is too big (but not accurate). | |
58 | */ | |
59 | int /* size for sf form */ | |
60 | xfs_dir2_block_sfsize( | |
61 | xfs_inode_t *dp, /* incore inode pointer */ | |
a2ceac1f | 62 | xfs_dir2_data_hdr_t *hdr, /* block directory data */ |
2bd0ea18 NS |
63 | xfs_dir2_sf_hdr_t *sfhp) /* output: header for sf form */ |
64 | { | |
65 | xfs_dir2_dataptr_t addr; /* data entry address */ | |
66 | xfs_dir2_leaf_entry_t *blp; /* leaf area of the block */ | |
67 | xfs_dir2_block_tail_t *btp; /* tail area of the block */ | |
68 | int count; /* shortform entry count */ | |
69 | xfs_dir2_data_entry_t *dep; /* data entry in the block */ | |
70 | int i; /* block entry index */ | |
71 | int i8count; /* count of big-inode entries */ | |
72 | int isdot; /* entry is "." */ | |
73 | int isdotdot; /* entry is ".." */ | |
74 | xfs_mount_t *mp; /* mount structure pointer */ | |
75 | int namelen; /* total name bytes */ | |
f9c48a87 | 76 | xfs_ino_t parent = 0; /* parent inode number */ |
0e266570 | 77 | int size=0; /* total computed size */ |
494434d7 | 78 | int has_ftype; |
ff105f75 | 79 | struct xfs_da_geometry *geo; |
2bd0ea18 NS |
80 | |
81 | mp = dp->i_mount; | |
ff105f75 | 82 | geo = mp->m_dir_geo; |
2bd0ea18 | 83 | |
494434d7 DC |
84 | /* |
85 | * if there is a filetype field, add the extra byte to the namelen | |
86 | * for each entry that we see. | |
87 | */ | |
88 | has_ftype = xfs_sb_version_hasftype(&mp->m_sb) ? 1 : 0; | |
89 | ||
2bd0ea18 | 90 | count = i8count = namelen = 0; |
ff105f75 | 91 | btp = xfs_dir2_block_tail_p(geo, hdr); |
5e656dbb | 92 | blp = xfs_dir2_block_leaf_p(btp); |
5000d01d | 93 | |
2bd0ea18 NS |
94 | /* |
95 | * Iterate over the block's data entries by using the leaf pointers. | |
96 | */ | |
5e656dbb BN |
97 | for (i = 0; i < be32_to_cpu(btp->count); i++) { |
98 | if ((addr = be32_to_cpu(blp[i].address)) == XFS_DIR2_NULL_DATAPTR) | |
2bd0ea18 NS |
99 | continue; |
100 | /* | |
101 | * Calculate the pointer to the entry at hand. | |
102 | */ | |
ff105f75 DC |
103 | dep = (xfs_dir2_data_entry_t *)((char *)hdr + |
104 | xfs_dir2_dataptr_to_off(geo, addr)); | |
2bd0ea18 NS |
105 | /* |
106 | * Detect . and .., so we can special-case them. | |
107 | * . is not included in sf directories. | |
108 | * .. is included by just the parent inode number. | |
109 | */ | |
110 | isdot = dep->namelen == 1 && dep->name[0] == '.'; | |
111 | isdotdot = | |
112 | dep->namelen == 2 && | |
113 | dep->name[0] == '.' && dep->name[1] == '.'; | |
5a35bf2c | 114 | |
2bd0ea18 | 115 | if (!isdot) |
5e656dbb | 116 | i8count += be64_to_cpu(dep->inumber) > XFS_DIR2_MAX_SHORT_INUM; |
5a35bf2c | 117 | |
494434d7 | 118 | /* take into account the file type field */ |
2bd0ea18 NS |
119 | if (!isdot && !isdotdot) { |
120 | count++; | |
494434d7 | 121 | namelen += dep->namelen + has_ftype; |
2bd0ea18 | 122 | } else if (isdotdot) |
5e656dbb | 123 | parent = be64_to_cpu(dep->inumber); |
2bd0ea18 NS |
124 | /* |
125 | * Calculate the new size, see if we should give up yet. | |
126 | */ | |
1faa1a81 | 127 | size = xfs_dir2_sf_hdr_size(i8count) + /* header */ |
4a492e72 | 128 | count * 3 * sizeof(u8) + /* namelen + offset */ |
1faa1a81 CH |
129 | namelen + /* name */ |
130 | (i8count ? /* inumber */ | |
d8bf9c63 CH |
131 | count * XFS_INO64_SIZE : |
132 | count * XFS_INO32_SIZE); | |
2bd0ea18 NS |
133 | if (size > XFS_IFORK_DSIZE(dp)) |
134 | return size; /* size value is a failure */ | |
135 | } | |
136 | /* | |
137 | * Create the output header, if it worked. | |
138 | */ | |
139 | sfhp->count = count; | |
140 | sfhp->i8count = i8count; | |
ff105f75 | 141 | dp->d_ops->sf_put_parent_ino(sfhp, parent); |
2bd0ea18 NS |
142 | return size; |
143 | } | |
144 | ||
145 | /* | |
146 | * Convert a block format directory to shortform. | |
147 | * Caller has already checked that it will fit, and built us a header. | |
148 | */ | |
149 | int /* error */ | |
150 | xfs_dir2_block_to_sf( | |
151 | xfs_da_args_t *args, /* operation arguments */ | |
a2ceac1f | 152 | struct xfs_buf *bp, |
2bd0ea18 NS |
153 | int size, /* shortform directory size */ |
154 | xfs_dir2_sf_hdr_t *sfhp) /* shortform directory hdr */ | |
155 | { | |
a2ceac1f | 156 | xfs_dir2_data_hdr_t *hdr; /* block header */ |
2bd0ea18 NS |
157 | xfs_dir2_data_entry_t *dep; /* data entry pointer */ |
158 | xfs_inode_t *dp; /* incore directory inode */ | |
159 | xfs_dir2_data_unused_t *dup; /* unused data pointer */ | |
160 | char *endptr; /* end of data entries */ | |
161 | int error; /* error return value */ | |
162 | int logflags; /* inode logging flags */ | |
163 | xfs_mount_t *mp; /* filesystem mount point */ | |
164 | char *ptr; /* current data pointer */ | |
165 | xfs_dir2_sf_entry_t *sfep; /* shortform entry */ | |
a2ceac1f | 166 | xfs_dir2_sf_hdr_t *sfp; /* shortform directory header */ |
ff105f75 | 167 | xfs_dir2_sf_hdr_t *dst; /* temporary data buffer */ |
5000d01d | 168 | |
56b2de80 DC |
169 | trace_xfs_dir2_block_to_sf(args); |
170 | ||
2bd0ea18 NS |
171 | dp = args->dp; |
172 | mp = dp->i_mount; | |
173 | ||
174 | /* | |
ff105f75 DC |
175 | * allocate a temporary destination buffer the size of the inode |
176 | * to format the data into. Once we have formatted the data, we | |
177 | * can free the block and copy the formatted data into the inode literal | |
178 | * area. | |
2bd0ea18 | 179 | */ |
ff105f75 DC |
180 | dst = kmem_alloc(mp->m_sb.sb_inodesize, KM_SLEEP); |
181 | hdr = bp->b_addr; | |
a2ceac1f | 182 | |
2bd0ea18 NS |
183 | /* |
184 | * Copy the header into the newly allocate local space. | |
185 | */ | |
ff105f75 | 186 | sfp = (xfs_dir2_sf_hdr_t *)dst; |
5e656dbb | 187 | memcpy(sfp, sfhp, xfs_dir2_sf_hdr_size(sfhp->i8count)); |
ff105f75 | 188 | |
2bd0ea18 NS |
189 | /* |
190 | * Set up to loop over the block's entries. | |
191 | */ | |
ff105f75 | 192 | ptr = (char *)dp->d_ops->data_entry_p(hdr); |
b67317ef | 193 | endptr = xfs_dir3_data_endp(args->geo, hdr); |
5e656dbb | 194 | sfep = xfs_dir2_sf_firstentry(sfp); |
2bd0ea18 NS |
195 | /* |
196 | * Loop over the active and unused entries. | |
197 | * Stop when we reach the leaf/tail portion of the block. | |
198 | */ | |
199 | while (ptr < endptr) { | |
200 | /* | |
201 | * If it's unused, just skip over it. | |
202 | */ | |
203 | dup = (xfs_dir2_data_unused_t *)ptr; | |
5e656dbb BN |
204 | if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) { |
205 | ptr += be16_to_cpu(dup->length); | |
2bd0ea18 NS |
206 | continue; |
207 | } | |
208 | dep = (xfs_dir2_data_entry_t *)ptr; | |
209 | /* | |
210 | * Skip . | |
211 | */ | |
212 | if (dep->namelen == 1 && dep->name[0] == '.') | |
5e656dbb | 213 | ASSERT(be64_to_cpu(dep->inumber) == dp->i_ino); |
2bd0ea18 NS |
214 | /* |
215 | * Skip .., but make sure the inode number is right. | |
216 | */ | |
217 | else if (dep->namelen == 2 && | |
218 | dep->name[0] == '.' && dep->name[1] == '.') | |
5e656dbb | 219 | ASSERT(be64_to_cpu(dep->inumber) == |
ff105f75 | 220 | dp->d_ops->sf_get_parent_ino(sfp)); |
2bd0ea18 NS |
221 | /* |
222 | * Normal entry, copy it into shortform. | |
223 | */ | |
224 | else { | |
225 | sfep->namelen = dep->namelen; | |
5e656dbb | 226 | xfs_dir2_sf_put_offset(sfep, |
2bd0ea18 | 227 | (xfs_dir2_data_aoff_t) |
a2ceac1f | 228 | ((char *)dep - (char *)hdr)); |
32181a02 | 229 | memcpy(sfep->name, dep->name, dep->namelen); |
ff105f75 DC |
230 | dp->d_ops->sf_put_ino(sfp, sfep, |
231 | be64_to_cpu(dep->inumber)); | |
232 | dp->d_ops->sf_put_ftype(sfep, | |
233 | dp->d_ops->data_get_ftype(dep)); | |
a2ceac1f | 234 | |
ff105f75 | 235 | sfep = dp->d_ops->sf_nextentry(sfp, sfep); |
2bd0ea18 | 236 | } |
ff105f75 | 237 | ptr += dp->d_ops->data_entsize(dep->namelen); |
2bd0ea18 NS |
238 | } |
239 | ASSERT((char *)sfep - (char *)sfp == size); | |
ff105f75 DC |
240 | |
241 | /* now we are done with the block, we can shrink the inode */ | |
242 | logflags = XFS_ILOG_CORE; | |
243 | error = xfs_dir2_shrink_inode(args, args->geo->datablk, bp); | |
244 | if (error) { | |
12b53197 | 245 | ASSERT(error != -ENOSPC); |
ff105f75 DC |
246 | goto out; |
247 | } | |
248 | ||
249 | /* | |
250 | * The buffer is now unconditionally gone, whether | |
251 | * xfs_dir2_shrink_inode worked or not. | |
252 | * | |
253 | * Convert the inode to local format and copy the data in. | |
254 | */ | |
ff105f75 | 255 | ASSERT(dp->i_df.if_bytes == 0); |
219eec76 CH |
256 | xfs_init_local_fork(dp, XFS_DATA_FORK, dst, size); |
257 | dp->i_d.di_format = XFS_DINODE_FMT_LOCAL; | |
258 | dp->i_d.di_size = size; | |
ff105f75 DC |
259 | |
260 | logflags |= XFS_ILOG_DDATA; | |
2bd0ea18 NS |
261 | xfs_dir2_sf_check(args); |
262 | out: | |
263 | xfs_trans_log_inode(args->trans, dp, logflags); | |
ff105f75 | 264 | kmem_free(dst); |
2bd0ea18 NS |
265 | return error; |
266 | } | |
267 | ||
268 | /* | |
269 | * Add a name to a shortform directory. | |
5000d01d | 270 | * There are two algorithms, "easy" and "hard" which we decide on |
2bd0ea18 NS |
271 | * before changing anything. |
272 | * Convert to block form if necessary, if the new entry won't fit. | |
273 | */ | |
274 | int /* error */ | |
275 | xfs_dir2_sf_addname( | |
276 | xfs_da_args_t *args) /* operation arguments */ | |
277 | { | |
2bd0ea18 NS |
278 | xfs_inode_t *dp; /* incore directory inode */ |
279 | int error; /* error return value */ | |
280 | int incr_isize; /* total change in size */ | |
281 | int new_isize; /* di_size after adding name */ | |
282 | int objchange; /* changing to 8-byte inodes */ | |
f9c48a87 | 283 | xfs_dir2_data_aoff_t offset = 0; /* offset for new entry */ |
2bd0ea18 | 284 | int pick; /* which algorithm to use */ |
a2ceac1f | 285 | xfs_dir2_sf_hdr_t *sfp; /* shortform structure */ |
f9c48a87 | 286 | xfs_dir2_sf_entry_t *sfep = NULL; /* shortform entry */ |
2bd0ea18 | 287 | |
56b2de80 DC |
288 | trace_xfs_dir2_sf_addname(args); |
289 | ||
12b53197 | 290 | ASSERT(xfs_dir2_sf_lookup(args) == -ENOENT); |
2bd0ea18 NS |
291 | dp = args->dp; |
292 | ASSERT(dp->i_df.if_flags & XFS_IFINLINE); | |
293 | /* | |
294 | * Make sure the shortform value has some of its header. | |
295 | */ | |
296 | if (dp->i_d.di_size < offsetof(xfs_dir2_sf_hdr_t, parent)) { | |
2bd0ea18 | 297 | ASSERT(XFS_FORCED_SHUTDOWN(dp->i_mount)); |
12b53197 | 298 | return -EIO; |
2bd0ea18 NS |
299 | } |
300 | ASSERT(dp->i_df.if_bytes == dp->i_d.di_size); | |
301 | ASSERT(dp->i_df.if_u1.if_data != NULL); | |
a2ceac1f DC |
302 | sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; |
303 | ASSERT(dp->i_d.di_size >= xfs_dir2_sf_hdr_size(sfp->i8count)); | |
2bd0ea18 NS |
304 | /* |
305 | * Compute entry (and change in) size. | |
306 | */ | |
ff105f75 | 307 | incr_isize = dp->d_ops->sf_entsize(sfp, args->namelen); |
2b288ccf | 308 | objchange = 0; |
5a35bf2c | 309 | |
2bd0ea18 NS |
310 | /* |
311 | * Do we have to change to 8 byte inodes? | |
312 | */ | |
a2ceac1f | 313 | if (args->inumber > XFS_DIR2_MAX_SHORT_INUM && sfp->i8count == 0) { |
2bd0ea18 | 314 | /* |
ff105f75 | 315 | * Yes, adjust the inode size. old count + (parent + new) |
2bd0ea18 | 316 | */ |
d8bf9c63 | 317 | incr_isize += (sfp->count + 2) * XFS_INO64_DIFF; |
2bd0ea18 | 318 | objchange = 1; |
2b288ccf | 319 | } |
5a35bf2c | 320 | |
ff105f75 | 321 | new_isize = (int)dp->i_d.di_size + incr_isize; |
2bd0ea18 NS |
322 | /* |
323 | * Won't fit as shortform any more (due to size), | |
324 | * or the pick routine says it won't (due to offset values). | |
325 | */ | |
326 | if (new_isize > XFS_IFORK_DSIZE(dp) || | |
327 | (pick = | |
328 | xfs_dir2_sf_addname_pick(args, objchange, &sfep, &offset)) == 0) { | |
2bd0ea18 NS |
329 | /* |
330 | * Just checking or no space reservation, it doesn't fit. | |
331 | */ | |
5e656dbb | 332 | if ((args->op_flags & XFS_DA_OP_JUSTCHECK) || args->total == 0) |
12b53197 | 333 | return -ENOSPC; |
2bd0ea18 NS |
334 | /* |
335 | * Convert to block form then add the name. | |
336 | */ | |
337 | error = xfs_dir2_sf_to_block(args); | |
338 | if (error) | |
339 | return error; | |
340 | return xfs_dir2_block_addname(args); | |
341 | } | |
342 | /* | |
343 | * Just checking, it fits. | |
344 | */ | |
5e656dbb | 345 | if (args->op_flags & XFS_DA_OP_JUSTCHECK) |
2bd0ea18 NS |
346 | return 0; |
347 | /* | |
348 | * Do it the easy way - just add it at the end. | |
349 | */ | |
350 | if (pick == 1) | |
351 | xfs_dir2_sf_addname_easy(args, sfep, offset, new_isize); | |
352 | /* | |
353 | * Do it the hard way - look for a place to insert the new entry. | |
354 | * Convert to 8 byte inode numbers first if necessary. | |
355 | */ | |
356 | else { | |
357 | ASSERT(pick == 2); | |
2bd0ea18 NS |
358 | if (objchange) |
359 | xfs_dir2_sf_toino8(args); | |
2bd0ea18 NS |
360 | xfs_dir2_sf_addname_hard(args, objchange, new_isize); |
361 | } | |
362 | xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA); | |
363 | return 0; | |
364 | } | |
365 | ||
366 | /* | |
367 | * Add the new entry the "easy" way. | |
368 | * This is copying the old directory and adding the new entry at the end. | |
369 | * Since it's sorted by "offset" we need room after the last offset | |
370 | * that's already there, and then room to convert to a block directory. | |
371 | * This is already checked by the pick routine. | |
372 | */ | |
5e656dbb | 373 | static void |
2bd0ea18 NS |
374 | xfs_dir2_sf_addname_easy( |
375 | xfs_da_args_t *args, /* operation arguments */ | |
376 | xfs_dir2_sf_entry_t *sfep, /* pointer to new entry */ | |
377 | xfs_dir2_data_aoff_t offset, /* offset to use for new ent */ | |
378 | int new_isize) /* new directory size */ | |
379 | { | |
380 | int byteoff; /* byte offset in sf dir */ | |
381 | xfs_inode_t *dp; /* incore directory inode */ | |
a2ceac1f | 382 | xfs_dir2_sf_hdr_t *sfp; /* shortform structure */ |
2bd0ea18 NS |
383 | |
384 | dp = args->dp; | |
385 | ||
a2ceac1f | 386 | sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; |
2bd0ea18 NS |
387 | byteoff = (int)((char *)sfep - (char *)sfp); |
388 | /* | |
389 | * Grow the in-inode space. | |
390 | */ | |
ff105f75 | 391 | xfs_idata_realloc(dp, dp->d_ops->sf_entsize(sfp, args->namelen), |
494434d7 | 392 | XFS_DATA_FORK); |
2bd0ea18 NS |
393 | /* |
394 | * Need to set up again due to realloc of the inode data. | |
395 | */ | |
a2ceac1f | 396 | sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; |
2bd0ea18 NS |
397 | sfep = (xfs_dir2_sf_entry_t *)((char *)sfp + byteoff); |
398 | /* | |
399 | * Fill in the new entry. | |
400 | */ | |
401 | sfep->namelen = args->namelen; | |
5e656dbb | 402 | xfs_dir2_sf_put_offset(sfep, offset); |
32181a02 | 403 | memcpy(sfep->name, args->name, sfep->namelen); |
ff105f75 DC |
404 | dp->d_ops->sf_put_ino(sfp, sfep, args->inumber); |
405 | dp->d_ops->sf_put_ftype(sfep, args->filetype); | |
906b762f | 406 | |
2bd0ea18 NS |
407 | /* |
408 | * Update the header and inode. | |
409 | */ | |
a2ceac1f | 410 | sfp->count++; |
2bd0ea18 | 411 | if (args->inumber > XFS_DIR2_MAX_SHORT_INUM) |
a2ceac1f | 412 | sfp->i8count++; |
2bd0ea18 NS |
413 | dp->i_d.di_size = new_isize; |
414 | xfs_dir2_sf_check(args); | |
415 | } | |
416 | ||
417 | /* | |
418 | * Add the new entry the "hard" way. | |
419 | * The caller has already converted to 8 byte inode numbers if necessary, | |
420 | * in which case we need to leave the i8count at 1. | |
421 | * Find a hole that the new entry will fit into, and copy | |
422 | * the first part of the entries, the new entry, and the last part of | |
423 | * the entries. | |
424 | */ | |
425 | /* ARGSUSED */ | |
5e656dbb | 426 | static void |
2bd0ea18 NS |
427 | xfs_dir2_sf_addname_hard( |
428 | xfs_da_args_t *args, /* operation arguments */ | |
429 | int objchange, /* changing inode number size */ | |
430 | int new_isize) /* new directory size */ | |
431 | { | |
432 | int add_datasize; /* data size need for new ent */ | |
d663096d | 433 | char *buf; /* buffer for old */ |
2bd0ea18 NS |
434 | xfs_inode_t *dp; /* incore directory inode */ |
435 | int eof; /* reached end of old dir */ | |
436 | int nbytes; /* temp for byte copies */ | |
437 | xfs_dir2_data_aoff_t new_offset; /* next offset value */ | |
438 | xfs_dir2_data_aoff_t offset; /* current offset value */ | |
439 | int old_isize; /* previous di_size */ | |
440 | xfs_dir2_sf_entry_t *oldsfep; /* entry in original dir */ | |
a2ceac1f | 441 | xfs_dir2_sf_hdr_t *oldsfp; /* original shortform dir */ |
2bd0ea18 | 442 | xfs_dir2_sf_entry_t *sfep; /* entry in new dir */ |
a2ceac1f | 443 | xfs_dir2_sf_hdr_t *sfp; /* new shortform dir */ |
2bd0ea18 NS |
444 | |
445 | /* | |
446 | * Copy the old directory to the stack buffer. | |
447 | */ | |
448 | dp = args->dp; | |
449 | ||
a2ceac1f | 450 | sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; |
2bd0ea18 | 451 | old_isize = (int)dp->i_d.di_size; |
d663096d | 452 | buf = kmem_alloc(old_isize, KM_SLEEP); |
a2ceac1f | 453 | oldsfp = (xfs_dir2_sf_hdr_t *)buf; |
32181a02 | 454 | memcpy(oldsfp, sfp, old_isize); |
2bd0ea18 NS |
455 | /* |
456 | * Loop over the old directory finding the place we're going | |
457 | * to insert the new entry. | |
458 | * If it's going to end up at the end then oldsfep will point there. | |
459 | */ | |
ff105f75 | 460 | for (offset = dp->d_ops->data_first_offset, |
5e656dbb | 461 | oldsfep = xfs_dir2_sf_firstentry(oldsfp), |
ff105f75 | 462 | add_datasize = dp->d_ops->data_entsize(args->namelen), |
2bd0ea18 NS |
463 | eof = (char *)oldsfep == &buf[old_isize]; |
464 | !eof; | |
ff105f75 DC |
465 | offset = new_offset + dp->d_ops->data_entsize(oldsfep->namelen), |
466 | oldsfep = dp->d_ops->sf_nextentry(oldsfp, oldsfep), | |
2bd0ea18 | 467 | eof = (char *)oldsfep == &buf[old_isize]) { |
5e656dbb | 468 | new_offset = xfs_dir2_sf_get_offset(oldsfep); |
2bd0ea18 NS |
469 | if (offset + add_datasize <= new_offset) |
470 | break; | |
471 | } | |
472 | /* | |
473 | * Get rid of the old directory, then allocate space for | |
dfc130f3 | 474 | * the new one. We do this so xfs_idata_realloc won't copy |
2bd0ea18 NS |
475 | * the data. |
476 | */ | |
477 | xfs_idata_realloc(dp, -old_isize, XFS_DATA_FORK); | |
478 | xfs_idata_realloc(dp, new_isize, XFS_DATA_FORK); | |
479 | /* | |
480 | * Reset the pointer since the buffer was reallocated. | |
481 | */ | |
a2ceac1f | 482 | sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; |
2bd0ea18 NS |
483 | /* |
484 | * Copy the first part of the directory, including the header. | |
485 | */ | |
486 | nbytes = (int)((char *)oldsfep - (char *)oldsfp); | |
32181a02 | 487 | memcpy(sfp, oldsfp, nbytes); |
2bd0ea18 NS |
488 | sfep = (xfs_dir2_sf_entry_t *)((char *)sfp + nbytes); |
489 | /* | |
490 | * Fill in the new entry, and update the header counts. | |
491 | */ | |
492 | sfep->namelen = args->namelen; | |
5e656dbb | 493 | xfs_dir2_sf_put_offset(sfep, offset); |
32181a02 | 494 | memcpy(sfep->name, args->name, sfep->namelen); |
ff105f75 DC |
495 | dp->d_ops->sf_put_ino(sfp, sfep, args->inumber); |
496 | dp->d_ops->sf_put_ftype(sfep, args->filetype); | |
a2ceac1f | 497 | sfp->count++; |
2bd0ea18 | 498 | if (args->inumber > XFS_DIR2_MAX_SHORT_INUM && !objchange) |
a2ceac1f | 499 | sfp->i8count++; |
2bd0ea18 NS |
500 | /* |
501 | * If there's more left to copy, do that. | |
502 | */ | |
503 | if (!eof) { | |
ff105f75 | 504 | sfep = dp->d_ops->sf_nextentry(sfp, sfep); |
32181a02 | 505 | memcpy(sfep, oldsfep, old_isize - nbytes); |
2bd0ea18 | 506 | } |
5e656dbb | 507 | kmem_free(buf); |
2bd0ea18 NS |
508 | dp->i_d.di_size = new_isize; |
509 | xfs_dir2_sf_check(args); | |
510 | } | |
511 | ||
512 | /* | |
513 | * Decide if the new entry will fit at all. | |
514 | * If it will fit, pick between adding the new entry to the end (easy) | |
515 | * or somewhere else (hard). | |
516 | * Return 0 (won't fit), 1 (easy), 2 (hard). | |
517 | */ | |
518 | /*ARGSUSED*/ | |
5e656dbb | 519 | static int /* pick result */ |
2bd0ea18 NS |
520 | xfs_dir2_sf_addname_pick( |
521 | xfs_da_args_t *args, /* operation arguments */ | |
522 | int objchange, /* inode # size changes */ | |
523 | xfs_dir2_sf_entry_t **sfepp, /* out(1): new entry ptr */ | |
524 | xfs_dir2_data_aoff_t *offsetp) /* out(1): new offset */ | |
525 | { | |
526 | xfs_inode_t *dp; /* incore directory inode */ | |
527 | int holefit; /* found hole it will fit in */ | |
528 | int i; /* entry number */ | |
2bd0ea18 NS |
529 | xfs_dir2_data_aoff_t offset; /* data block offset */ |
530 | xfs_dir2_sf_entry_t *sfep; /* shortform entry */ | |
a2ceac1f | 531 | xfs_dir2_sf_hdr_t *sfp; /* shortform structure */ |
2bd0ea18 NS |
532 | int size; /* entry's data size */ |
533 | int used; /* data bytes used */ | |
534 | ||
535 | dp = args->dp; | |
2bd0ea18 | 536 | |
a2ceac1f | 537 | sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; |
ff105f75 DC |
538 | size = dp->d_ops->data_entsize(args->namelen); |
539 | offset = dp->d_ops->data_first_offset; | |
5e656dbb | 540 | sfep = xfs_dir2_sf_firstentry(sfp); |
2bd0ea18 NS |
541 | holefit = 0; |
542 | /* | |
543 | * Loop over sf entries. | |
544 | * Keep track of data offset and whether we've seen a place | |
545 | * to insert the new entry. | |
546 | */ | |
a2ceac1f | 547 | for (i = 0; i < sfp->count; i++) { |
2bd0ea18 | 548 | if (!holefit) |
5e656dbb BN |
549 | holefit = offset + size <= xfs_dir2_sf_get_offset(sfep); |
550 | offset = xfs_dir2_sf_get_offset(sfep) + | |
ff105f75 DC |
551 | dp->d_ops->data_entsize(sfep->namelen); |
552 | sfep = dp->d_ops->sf_nextentry(sfp, sfep); | |
2bd0ea18 NS |
553 | } |
554 | /* | |
555 | * Calculate data bytes used excluding the new entry, if this | |
556 | * was a data block (block form directory). | |
557 | */ | |
558 | used = offset + | |
a2ceac1f | 559 | (sfp->count + 3) * (uint)sizeof(xfs_dir2_leaf_entry_t) + |
2bd0ea18 NS |
560 | (uint)sizeof(xfs_dir2_block_tail_t); |
561 | /* | |
562 | * If it won't fit in a block form then we can't insert it, | |
563 | * we'll go back, convert to block, then try the insert and convert | |
564 | * to leaf. | |
565 | */ | |
ff105f75 | 566 | if (used + (holefit ? 0 : size) > args->geo->blksize) |
2bd0ea18 NS |
567 | return 0; |
568 | /* | |
569 | * If changing the inode number size, do it the hard way. | |
570 | */ | |
5a35bf2c | 571 | if (objchange) |
2bd0ea18 | 572 | return 2; |
2bd0ea18 NS |
573 | /* |
574 | * If it won't fit at the end then do it the hard way (use the hole). | |
575 | */ | |
ff105f75 | 576 | if (used + size > args->geo->blksize) |
2bd0ea18 NS |
577 | return 2; |
578 | /* | |
579 | * Do it the easy way. | |
580 | */ | |
581 | *sfepp = sfep; | |
582 | *offsetp = offset; | |
583 | return 1; | |
584 | } | |
585 | ||
586 | #ifdef DEBUG | |
587 | /* | |
588 | * Check consistency of shortform directory, assert if bad. | |
589 | */ | |
5e656dbb | 590 | static void |
2bd0ea18 NS |
591 | xfs_dir2_sf_check( |
592 | xfs_da_args_t *args) /* operation arguments */ | |
593 | { | |
594 | xfs_inode_t *dp; /* incore directory inode */ | |
595 | int i; /* entry number */ | |
596 | int i8count; /* number of big inode#s */ | |
597 | xfs_ino_t ino; /* entry inode number */ | |
598 | int offset; /* data offset */ | |
599 | xfs_dir2_sf_entry_t *sfep; /* shortform dir entry */ | |
a2ceac1f | 600 | xfs_dir2_sf_hdr_t *sfp; /* shortform structure */ |
2bd0ea18 NS |
601 | |
602 | dp = args->dp; | |
603 | ||
a2ceac1f | 604 | sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; |
ff105f75 DC |
605 | offset = dp->d_ops->data_first_offset; |
606 | ino = dp->d_ops->sf_get_parent_ino(sfp); | |
2bd0ea18 | 607 | i8count = ino > XFS_DIR2_MAX_SHORT_INUM; |
5000d01d | 608 | |
5e656dbb | 609 | for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); |
a2ceac1f | 610 | i < sfp->count; |
ff105f75 | 611 | i++, sfep = dp->d_ops->sf_nextentry(sfp, sfep)) { |
5e656dbb | 612 | ASSERT(xfs_dir2_sf_get_offset(sfep) >= offset); |
ff105f75 | 613 | ino = dp->d_ops->sf_get_ino(sfp, sfep); |
2bd0ea18 NS |
614 | i8count += ino > XFS_DIR2_MAX_SHORT_INUM; |
615 | offset = | |
5e656dbb | 616 | xfs_dir2_sf_get_offset(sfep) + |
ff105f75 DC |
617 | dp->d_ops->data_entsize(sfep->namelen); |
618 | ASSERT(dp->d_ops->sf_get_ftype(sfep) < XFS_DIR3_FT_MAX); | |
2bd0ea18 | 619 | } |
a2ceac1f | 620 | ASSERT(i8count == sfp->i8count); |
2bd0ea18 NS |
621 | ASSERT((char *)sfep - (char *)sfp == dp->i_d.di_size); |
622 | ASSERT(offset + | |
a2ceac1f | 623 | (sfp->count + 2) * (uint)sizeof(xfs_dir2_leaf_entry_t) + |
ff105f75 | 624 | (uint)sizeof(xfs_dir2_block_tail_t) <= args->geo->blksize); |
2bd0ea18 NS |
625 | } |
626 | #endif /* DEBUG */ | |
627 | ||
fc1d6454 | 628 | /* Verify the consistency of an inline directory. */ |
1f98251f | 629 | xfs_failaddr_t |
fc1d6454 | 630 | xfs_dir2_sf_verify( |
d15188a1 | 631 | struct xfs_inode *ip) |
fc1d6454 | 632 | { |
d15188a1 DW |
633 | struct xfs_mount *mp = ip->i_mount; |
634 | struct xfs_dir2_sf_hdr *sfp; | |
fc1d6454 DW |
635 | struct xfs_dir2_sf_entry *sfep; |
636 | struct xfs_dir2_sf_entry *next_sfep; | |
637 | char *endp; | |
638 | const struct xfs_dir_ops *dops; | |
d15188a1 | 639 | struct xfs_ifork *ifp; |
fc1d6454 DW |
640 | xfs_ino_t ino; |
641 | int i; | |
642 | int i8count; | |
643 | int offset; | |
d15188a1 DW |
644 | int size; |
645 | int error; | |
4a492e72 | 646 | uint8_t filetype; |
fc1d6454 | 647 | |
d15188a1 DW |
648 | ASSERT(ip->i_d.di_format == XFS_DINODE_FMT_LOCAL); |
649 | /* | |
650 | * xfs_iread calls us before xfs_setup_inode sets up ip->d_ops, | |
651 | * so we can only trust the mountpoint to have the right pointer. | |
652 | */ | |
fc1d6454 DW |
653 | dops = xfs_dir_get_ops(mp, NULL); |
654 | ||
d15188a1 DW |
655 | ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK); |
656 | sfp = (struct xfs_dir2_sf_hdr *)ifp->if_u1.if_data; | |
657 | size = ifp->if_bytes; | |
658 | ||
fc1d6454 DW |
659 | /* |
660 | * Give up if the directory is way too short. | |
661 | */ | |
d15188a1 DW |
662 | if (size <= offsetof(struct xfs_dir2_sf_hdr, parent) || |
663 | size < xfs_dir2_sf_hdr_size(sfp->i8count)) | |
1f98251f | 664 | return __this_address; |
fc1d6454 DW |
665 | |
666 | endp = (char *)sfp + size; | |
667 | ||
668 | /* Check .. entry */ | |
669 | ino = dops->sf_get_parent_ino(sfp); | |
670 | i8count = ino > XFS_DIR2_MAX_SHORT_INUM; | |
d15188a1 DW |
671 | error = xfs_dir_ino_validate(mp, ino); |
672 | if (error) | |
1f98251f | 673 | return __this_address; |
fc1d6454 DW |
674 | offset = dops->data_first_offset; |
675 | ||
676 | /* Check all reported entries */ | |
677 | sfep = xfs_dir2_sf_firstentry(sfp); | |
678 | for (i = 0; i < sfp->count; i++) { | |
679 | /* | |
680 | * struct xfs_dir2_sf_entry has a variable length. | |
681 | * Check the fixed-offset parts of the structure are | |
682 | * within the data buffer. | |
683 | */ | |
d15188a1 | 684 | if (((char *)sfep + sizeof(*sfep)) >= endp) |
1f98251f | 685 | return __this_address; |
fc1d6454 DW |
686 | |
687 | /* Don't allow names with known bad length. */ | |
d15188a1 | 688 | if (sfep->namelen == 0) |
1f98251f | 689 | return __this_address; |
fc1d6454 DW |
690 | |
691 | /* | |
692 | * Check that the variable-length part of the structure is | |
693 | * within the data buffer. The next entry starts after the | |
694 | * name component, so nextentry is an acceptable test. | |
695 | */ | |
696 | next_sfep = dops->sf_nextentry(sfp, sfep); | |
d15188a1 | 697 | if (endp < (char *)next_sfep) |
1f98251f | 698 | return __this_address; |
fc1d6454 DW |
699 | |
700 | /* Check that the offsets always increase. */ | |
d15188a1 | 701 | if (xfs_dir2_sf_get_offset(sfep) < offset) |
1f98251f | 702 | return __this_address; |
fc1d6454 DW |
703 | |
704 | /* Check the inode number. */ | |
705 | ino = dops->sf_get_ino(sfp, sfep); | |
706 | i8count += ino > XFS_DIR2_MAX_SHORT_INUM; | |
d15188a1 DW |
707 | error = xfs_dir_ino_validate(mp, ino); |
708 | if (error) | |
1f98251f | 709 | return __this_address; |
fc1d6454 DW |
710 | |
711 | /* Check the file type. */ | |
712 | filetype = dops->sf_get_ftype(sfep); | |
d15188a1 | 713 | if (filetype >= XFS_DIR3_FT_MAX) |
1f98251f | 714 | return __this_address; |
fc1d6454 DW |
715 | |
716 | offset = xfs_dir2_sf_get_offset(sfep) + | |
717 | dops->data_entsize(sfep->namelen); | |
718 | ||
719 | sfep = next_sfep; | |
720 | } | |
d15188a1 | 721 | if (i8count != sfp->i8count) |
1f98251f | 722 | return __this_address; |
d15188a1 | 723 | if ((void *)sfep != (void *)endp) |
1f98251f | 724 | return __this_address; |
fc1d6454 DW |
725 | |
726 | /* Make sure this whole thing ought to be in local format. */ | |
d15188a1 DW |
727 | if (offset + (sfp->count + 2) * (uint)sizeof(xfs_dir2_leaf_entry_t) + |
728 | (uint)sizeof(xfs_dir2_block_tail_t) > mp->m_dir_geo->blksize) | |
1f98251f | 729 | return __this_address; |
fc1d6454 | 730 | |
1f98251f | 731 | return NULL; |
fc1d6454 DW |
732 | } |
733 | ||
5000d01d | 734 | /* |
2bd0ea18 NS |
735 | * Create a new (shortform) directory. |
736 | */ | |
737 | int /* error, always 0 */ | |
738 | xfs_dir2_sf_create( | |
739 | xfs_da_args_t *args, /* operation arguments */ | |
740 | xfs_ino_t pino) /* parent inode number */ | |
741 | { | |
742 | xfs_inode_t *dp; /* incore directory inode */ | |
743 | int i8count; /* parent inode is an 8-byte number */ | |
a2ceac1f | 744 | xfs_dir2_sf_hdr_t *sfp; /* shortform structure */ |
2bd0ea18 NS |
745 | int size; /* directory size */ |
746 | ||
56b2de80 DC |
747 | trace_xfs_dir2_sf_create(args); |
748 | ||
2bd0ea18 NS |
749 | dp = args->dp; |
750 | ||
751 | ASSERT(dp != NULL); | |
752 | ASSERT(dp->i_d.di_size == 0); | |
753 | /* | |
5000d01d | 754 | * If it's currently a zero-length extent file, |
2bd0ea18 NS |
755 | * convert it to local format. |
756 | */ | |
757 | if (dp->i_d.di_format == XFS_DINODE_FMT_EXTENTS) { | |
758 | dp->i_df.if_flags &= ~XFS_IFEXTENTS; /* just in case */ | |
759 | dp->i_d.di_format = XFS_DINODE_FMT_LOCAL; | |
760 | xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE); | |
761 | dp->i_df.if_flags |= XFS_IFINLINE; | |
762 | } | |
763 | ASSERT(dp->i_df.if_flags & XFS_IFINLINE); | |
764 | ASSERT(dp->i_df.if_bytes == 0); | |
765 | i8count = pino > XFS_DIR2_MAX_SHORT_INUM; | |
5e656dbb | 766 | size = xfs_dir2_sf_hdr_size(i8count); |
2bd0ea18 NS |
767 | /* |
768 | * Make a buffer for the data. | |
769 | */ | |
770 | xfs_idata_realloc(dp, size, XFS_DATA_FORK); | |
771 | /* | |
772 | * Fill in the header, | |
773 | */ | |
a2ceac1f DC |
774 | sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; |
775 | sfp->i8count = i8count; | |
2bd0ea18 NS |
776 | /* |
777 | * Now can put in the inode number, since i8count is set. | |
778 | */ | |
ff105f75 | 779 | dp->d_ops->sf_put_parent_ino(sfp, pino); |
a2ceac1f | 780 | sfp->count = 0; |
2bd0ea18 NS |
781 | dp->i_d.di_size = size; |
782 | xfs_dir2_sf_check(args); | |
783 | xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA); | |
784 | return 0; | |
785 | } | |
786 | ||
787 | /* | |
788 | * Lookup an entry in a shortform directory. | |
789 | * Returns EEXIST if found, ENOENT if not found. | |
790 | */ | |
791 | int /* error */ | |
792 | xfs_dir2_sf_lookup( | |
793 | xfs_da_args_t *args) /* operation arguments */ | |
794 | { | |
795 | xfs_inode_t *dp; /* incore directory inode */ | |
796 | int i; /* entry index */ | |
5e656dbb | 797 | int error; |
2bd0ea18 | 798 | xfs_dir2_sf_entry_t *sfep; /* shortform directory entry */ |
a2ceac1f | 799 | xfs_dir2_sf_hdr_t *sfp; /* shortform structure */ |
5e656dbb BN |
800 | enum xfs_dacmp cmp; /* comparison result */ |
801 | xfs_dir2_sf_entry_t *ci_sfep; /* case-insens. entry */ | |
2bd0ea18 | 802 | |
56b2de80 DC |
803 | trace_xfs_dir2_sf_lookup(args); |
804 | ||
2bd0ea18 NS |
805 | xfs_dir2_sf_check(args); |
806 | dp = args->dp; | |
807 | ||
808 | ASSERT(dp->i_df.if_flags & XFS_IFINLINE); | |
809 | /* | |
810 | * Bail out if the directory is way too short. | |
811 | */ | |
812 | if (dp->i_d.di_size < offsetof(xfs_dir2_sf_hdr_t, parent)) { | |
2bd0ea18 | 813 | ASSERT(XFS_FORCED_SHUTDOWN(dp->i_mount)); |
12b53197 | 814 | return -EIO; |
2bd0ea18 NS |
815 | } |
816 | ASSERT(dp->i_df.if_bytes == dp->i_d.di_size); | |
817 | ASSERT(dp->i_df.if_u1.if_data != NULL); | |
a2ceac1f DC |
818 | sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; |
819 | ASSERT(dp->i_d.di_size >= xfs_dir2_sf_hdr_size(sfp->i8count)); | |
2bd0ea18 NS |
820 | /* |
821 | * Special case for . | |
822 | */ | |
823 | if (args->namelen == 1 && args->name[0] == '.') { | |
824 | args->inumber = dp->i_ino; | |
5e656dbb | 825 | args->cmpresult = XFS_CMP_EXACT; |
906b762f | 826 | args->filetype = XFS_DIR3_FT_DIR; |
12b53197 | 827 | return -EEXIST; |
2bd0ea18 NS |
828 | } |
829 | /* | |
830 | * Special case for .. | |
831 | */ | |
832 | if (args->namelen == 2 && | |
833 | args->name[0] == '.' && args->name[1] == '.') { | |
ff105f75 | 834 | args->inumber = dp->d_ops->sf_get_parent_ino(sfp); |
5e656dbb | 835 | args->cmpresult = XFS_CMP_EXACT; |
906b762f | 836 | args->filetype = XFS_DIR3_FT_DIR; |
12b53197 | 837 | return -EEXIST; |
2bd0ea18 NS |
838 | } |
839 | /* | |
840 | * Loop over all the entries trying to match ours. | |
841 | */ | |
5e656dbb | 842 | ci_sfep = NULL; |
a2ceac1f | 843 | for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->count; |
ff105f75 | 844 | i++, sfep = dp->d_ops->sf_nextentry(sfp, sfep)) { |
5e656dbb BN |
845 | /* |
846 | * Compare name and if it's an exact match, return the inode | |
847 | * number. If it's the first case-insensitive match, store the | |
848 | * inode number and continue looking for an exact match. | |
849 | */ | |
850 | cmp = dp->i_mount->m_dirnameops->compname(args, sfep->name, | |
851 | sfep->namelen); | |
852 | if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) { | |
853 | args->cmpresult = cmp; | |
ff105f75 DC |
854 | args->inumber = dp->d_ops->sf_get_ino(sfp, sfep); |
855 | args->filetype = dp->d_ops->sf_get_ftype(sfep); | |
5e656dbb | 856 | if (cmp == XFS_CMP_EXACT) |
12b53197 | 857 | return -EEXIST; |
5e656dbb | 858 | ci_sfep = sfep; |
2bd0ea18 NS |
859 | } |
860 | } | |
5e656dbb | 861 | ASSERT(args->op_flags & XFS_DA_OP_OKNOENT); |
2bd0ea18 | 862 | /* |
5e656dbb | 863 | * Here, we can only be doing a lookup (not a rename or replace). |
12b53197 | 864 | * If a case-insensitive match was not found, return -ENOENT. |
2bd0ea18 | 865 | */ |
5e656dbb | 866 | if (!ci_sfep) |
12b53197 | 867 | return -ENOENT; |
5e656dbb BN |
868 | /* otherwise process the CI match as required by the caller */ |
869 | error = xfs_dir_cilookup_result(args, ci_sfep->name, ci_sfep->namelen); | |
1e68581b | 870 | return error; |
2bd0ea18 NS |
871 | } |
872 | ||
873 | /* | |
874 | * Remove an entry from a shortform directory. | |
875 | */ | |
876 | int /* error */ | |
877 | xfs_dir2_sf_removename( | |
878 | xfs_da_args_t *args) | |
879 | { | |
880 | int byteoff; /* offset of removed entry */ | |
881 | xfs_inode_t *dp; /* incore directory inode */ | |
882 | int entsize; /* this entry's size */ | |
883 | int i; /* shortform entry index */ | |
884 | int newsize; /* new inode size */ | |
885 | int oldsize; /* old inode size */ | |
886 | xfs_dir2_sf_entry_t *sfep; /* shortform directory entry */ | |
a2ceac1f | 887 | xfs_dir2_sf_hdr_t *sfp; /* shortform structure */ |
2bd0ea18 | 888 | |
56b2de80 DC |
889 | trace_xfs_dir2_sf_removename(args); |
890 | ||
2bd0ea18 NS |
891 | dp = args->dp; |
892 | ||
893 | ASSERT(dp->i_df.if_flags & XFS_IFINLINE); | |
894 | oldsize = (int)dp->i_d.di_size; | |
895 | /* | |
896 | * Bail out if the directory is way too short. | |
897 | */ | |
898 | if (oldsize < offsetof(xfs_dir2_sf_hdr_t, parent)) { | |
2bd0ea18 | 899 | ASSERT(XFS_FORCED_SHUTDOWN(dp->i_mount)); |
12b53197 | 900 | return -EIO; |
2bd0ea18 NS |
901 | } |
902 | ASSERT(dp->i_df.if_bytes == oldsize); | |
903 | ASSERT(dp->i_df.if_u1.if_data != NULL); | |
a2ceac1f DC |
904 | sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; |
905 | ASSERT(oldsize >= xfs_dir2_sf_hdr_size(sfp->i8count)); | |
2bd0ea18 NS |
906 | /* |
907 | * Loop over the old directory entries. | |
908 | * Find the one we're deleting. | |
909 | */ | |
a2ceac1f | 910 | for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->count; |
ff105f75 | 911 | i++, sfep = dp->d_ops->sf_nextentry(sfp, sfep)) { |
5e656dbb BN |
912 | if (xfs_da_compname(args, sfep->name, sfep->namelen) == |
913 | XFS_CMP_EXACT) { | |
ff105f75 | 914 | ASSERT(dp->d_ops->sf_get_ino(sfp, sfep) == |
a2ceac1f | 915 | args->inumber); |
2bd0ea18 NS |
916 | break; |
917 | } | |
918 | } | |
919 | /* | |
920 | * Didn't find it. | |
921 | */ | |
a2ceac1f | 922 | if (i == sfp->count) |
12b53197 | 923 | return -ENOENT; |
2bd0ea18 NS |
924 | /* |
925 | * Calculate sizes. | |
926 | */ | |
927 | byteoff = (int)((char *)sfep - (char *)sfp); | |
ff105f75 | 928 | entsize = dp->d_ops->sf_entsize(sfp, args->namelen); |
2bd0ea18 NS |
929 | newsize = oldsize - entsize; |
930 | /* | |
931 | * Copy the part if any after the removed entry, sliding it down. | |
932 | */ | |
933 | if (byteoff + entsize < oldsize) | |
32181a02 | 934 | memmove((char *)sfp + byteoff, (char *)sfp + byteoff + entsize, |
2bd0ea18 NS |
935 | oldsize - (byteoff + entsize)); |
936 | /* | |
937 | * Fix up the header and file size. | |
938 | */ | |
a2ceac1f | 939 | sfp->count--; |
2bd0ea18 NS |
940 | dp->i_d.di_size = newsize; |
941 | /* | |
942 | * Reallocate, making it smaller. | |
943 | */ | |
944 | xfs_idata_realloc(dp, newsize - oldsize, XFS_DATA_FORK); | |
a2ceac1f | 945 | sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; |
2bd0ea18 NS |
946 | /* |
947 | * Are we changing inode number size? | |
948 | */ | |
949 | if (args->inumber > XFS_DIR2_MAX_SHORT_INUM) { | |
a2ceac1f | 950 | if (sfp->i8count == 1) |
2bd0ea18 NS |
951 | xfs_dir2_sf_toino4(args); |
952 | else | |
a2ceac1f | 953 | sfp->i8count--; |
2bd0ea18 | 954 | } |
2bd0ea18 NS |
955 | xfs_dir2_sf_check(args); |
956 | xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA); | |
957 | return 0; | |
958 | } | |
959 | ||
960 | /* | |
961 | * Replace the inode number of an entry in a shortform directory. | |
962 | */ | |
963 | int /* error */ | |
964 | xfs_dir2_sf_replace( | |
965 | xfs_da_args_t *args) /* operation arguments */ | |
966 | { | |
967 | xfs_inode_t *dp; /* incore directory inode */ | |
968 | int i; /* entry index */ | |
0e266570 | 969 | xfs_ino_t ino=0; /* entry old inode number */ |
2e72c460 | 970 | int i8elevated; /* sf_toino8 set i8count=1 */ |
2bd0ea18 | 971 | xfs_dir2_sf_entry_t *sfep; /* shortform directory entry */ |
a2ceac1f | 972 | xfs_dir2_sf_hdr_t *sfp; /* shortform structure */ |
2bd0ea18 | 973 | |
56b2de80 DC |
974 | trace_xfs_dir2_sf_replace(args); |
975 | ||
2bd0ea18 NS |
976 | dp = args->dp; |
977 | ||
978 | ASSERT(dp->i_df.if_flags & XFS_IFINLINE); | |
979 | /* | |
980 | * Bail out if the shortform directory is way too small. | |
981 | */ | |
982 | if (dp->i_d.di_size < offsetof(xfs_dir2_sf_hdr_t, parent)) { | |
2bd0ea18 | 983 | ASSERT(XFS_FORCED_SHUTDOWN(dp->i_mount)); |
12b53197 | 984 | return -EIO; |
2bd0ea18 NS |
985 | } |
986 | ASSERT(dp->i_df.if_bytes == dp->i_d.di_size); | |
987 | ASSERT(dp->i_df.if_u1.if_data != NULL); | |
a2ceac1f DC |
988 | sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; |
989 | ASSERT(dp->i_d.di_size >= xfs_dir2_sf_hdr_size(sfp->i8count)); | |
5a35bf2c | 990 | |
2bd0ea18 NS |
991 | /* |
992 | * New inode number is large, and need to convert to 8-byte inodes. | |
993 | */ | |
a2ceac1f | 994 | if (args->inumber > XFS_DIR2_MAX_SHORT_INUM && sfp->i8count == 0) { |
2bd0ea18 NS |
995 | int error; /* error return value */ |
996 | int newsize; /* new inode size */ | |
997 | ||
d8bf9c63 | 998 | newsize = dp->i_df.if_bytes + (sfp->count + 1) * XFS_INO64_DIFF; |
2bd0ea18 NS |
999 | /* |
1000 | * Won't fit as shortform, convert to block then do replace. | |
1001 | */ | |
1002 | if (newsize > XFS_IFORK_DSIZE(dp)) { | |
1003 | error = xfs_dir2_sf_to_block(args); | |
1004 | if (error) { | |
1005 | return error; | |
1006 | } | |
1007 | return xfs_dir2_block_replace(args); | |
1008 | } | |
1009 | /* | |
1010 | * Still fits, convert to 8-byte now. | |
1011 | */ | |
1012 | xfs_dir2_sf_toino8(args); | |
2e72c460 | 1013 | i8elevated = 1; |
a2ceac1f | 1014 | sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; |
2e72c460 ES |
1015 | } else |
1016 | i8elevated = 0; | |
5a35bf2c | 1017 | |
2bd0ea18 NS |
1018 | ASSERT(args->namelen != 1 || args->name[0] != '.'); |
1019 | /* | |
1020 | * Replace ..'s entry. | |
1021 | */ | |
1022 | if (args->namelen == 2 && | |
1023 | args->name[0] == '.' && args->name[1] == '.') { | |
ff105f75 | 1024 | ino = dp->d_ops->sf_get_parent_ino(sfp); |
2bd0ea18 | 1025 | ASSERT(args->inumber != ino); |
ff105f75 | 1026 | dp->d_ops->sf_put_parent_ino(sfp, args->inumber); |
2bd0ea18 NS |
1027 | } |
1028 | /* | |
1029 | * Normal entry, look for the name. | |
1030 | */ | |
1031 | else { | |
494434d7 | 1032 | for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->count; |
ff105f75 | 1033 | i++, sfep = dp->d_ops->sf_nextentry(sfp, sfep)) { |
5e656dbb BN |
1034 | if (xfs_da_compname(args, sfep->name, sfep->namelen) == |
1035 | XFS_CMP_EXACT) { | |
ff105f75 | 1036 | ino = dp->d_ops->sf_get_ino(sfp, sfep); |
2bd0ea18 | 1037 | ASSERT(args->inumber != ino); |
ff105f75 DC |
1038 | dp->d_ops->sf_put_ino(sfp, sfep, args->inumber); |
1039 | dp->d_ops->sf_put_ftype(sfep, args->filetype); | |
2bd0ea18 NS |
1040 | break; |
1041 | } | |
1042 | } | |
1043 | /* | |
1044 | * Didn't find it. | |
1045 | */ | |
a2ceac1f | 1046 | if (i == sfp->count) { |
5e656dbb | 1047 | ASSERT(args->op_flags & XFS_DA_OP_OKNOENT); |
2e72c460 ES |
1048 | if (i8elevated) |
1049 | xfs_dir2_sf_toino4(args); | |
12b53197 | 1050 | return -ENOENT; |
2bd0ea18 NS |
1051 | } |
1052 | } | |
2bd0ea18 NS |
1053 | /* |
1054 | * See if the old number was large, the new number is small. | |
1055 | */ | |
1056 | if (ino > XFS_DIR2_MAX_SHORT_INUM && | |
1057 | args->inumber <= XFS_DIR2_MAX_SHORT_INUM) { | |
2bd0ea18 NS |
1058 | /* |
1059 | * And the old count was one, so need to convert to small. | |
1060 | */ | |
a2ceac1f | 1061 | if (sfp->i8count == 1) |
2bd0ea18 NS |
1062 | xfs_dir2_sf_toino4(args); |
1063 | else | |
a2ceac1f | 1064 | sfp->i8count--; |
2bd0ea18 | 1065 | } |
2e72c460 ES |
1066 | /* |
1067 | * See if the old number was small, the new number is large. | |
1068 | */ | |
1069 | if (ino <= XFS_DIR2_MAX_SHORT_INUM && | |
1070 | args->inumber > XFS_DIR2_MAX_SHORT_INUM) { | |
1071 | /* | |
1072 | * add to the i8count unless we just converted to 8-byte | |
1073 | * inodes (which does an implied i8count = 1) | |
1074 | */ | |
a2ceac1f | 1075 | ASSERT(sfp->i8count != 0); |
2e72c460 | 1076 | if (!i8elevated) |
a2ceac1f | 1077 | sfp->i8count++; |
2e72c460 | 1078 | } |
2bd0ea18 NS |
1079 | xfs_dir2_sf_check(args); |
1080 | xfs_trans_log_inode(args->trans, dp, XFS_ILOG_DDATA); | |
1081 | return 0; | |
1082 | } | |
1083 | ||
2bd0ea18 NS |
1084 | /* |
1085 | * Convert from 8-byte inode numbers to 4-byte inode numbers. | |
1086 | * The last 8-byte inode number is gone, but the count is still 1. | |
1087 | */ | |
5e656dbb | 1088 | static void |
2bd0ea18 NS |
1089 | xfs_dir2_sf_toino4( |
1090 | xfs_da_args_t *args) /* operation arguments */ | |
1091 | { | |
1092 | char *buf; /* old dir's buffer */ | |
1093 | xfs_inode_t *dp; /* incore directory inode */ | |
1094 | int i; /* entry index */ | |
2bd0ea18 NS |
1095 | int newsize; /* new inode size */ |
1096 | xfs_dir2_sf_entry_t *oldsfep; /* old sf entry */ | |
a2ceac1f | 1097 | xfs_dir2_sf_hdr_t *oldsfp; /* old sf directory */ |
2bd0ea18 NS |
1098 | int oldsize; /* old inode size */ |
1099 | xfs_dir2_sf_entry_t *sfep; /* new sf entry */ | |
a2ceac1f | 1100 | xfs_dir2_sf_hdr_t *sfp; /* new sf directory */ |
2bd0ea18 | 1101 | |
56b2de80 DC |
1102 | trace_xfs_dir2_sf_toino4(args); |
1103 | ||
2bd0ea18 NS |
1104 | dp = args->dp; |
1105 | ||
1106 | /* | |
1107 | * Copy the old directory to the buffer. | |
1108 | * Then nuke it from the inode, and add the new buffer to the inode. | |
1109 | * Don't want xfs_idata_realloc copying the data here. | |
1110 | */ | |
1111 | oldsize = dp->i_df.if_bytes; | |
1112 | buf = kmem_alloc(oldsize, KM_SLEEP); | |
a2ceac1f DC |
1113 | oldsfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; |
1114 | ASSERT(oldsfp->i8count == 1); | |
32181a02 | 1115 | memcpy(buf, oldsfp, oldsize); |
2bd0ea18 NS |
1116 | /* |
1117 | * Compute the new inode size. | |
1118 | */ | |
d8bf9c63 | 1119 | newsize = oldsize - (oldsfp->count + 1) * XFS_INO64_DIFF; |
2bd0ea18 NS |
1120 | xfs_idata_realloc(dp, -oldsize, XFS_DATA_FORK); |
1121 | xfs_idata_realloc(dp, newsize, XFS_DATA_FORK); | |
1122 | /* | |
1123 | * Reset our pointers, the data has moved. | |
1124 | */ | |
a2ceac1f DC |
1125 | oldsfp = (xfs_dir2_sf_hdr_t *)buf; |
1126 | sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; | |
2bd0ea18 NS |
1127 | /* |
1128 | * Fill in the new header. | |
1129 | */ | |
a2ceac1f DC |
1130 | sfp->count = oldsfp->count; |
1131 | sfp->i8count = 0; | |
ff105f75 | 1132 | dp->d_ops->sf_put_parent_ino(sfp, dp->d_ops->sf_get_parent_ino(oldsfp)); |
2bd0ea18 NS |
1133 | /* |
1134 | * Copy the entries field by field. | |
1135 | */ | |
5e656dbb BN |
1136 | for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp), |
1137 | oldsfep = xfs_dir2_sf_firstentry(oldsfp); | |
a2ceac1f | 1138 | i < sfp->count; |
ff105f75 DC |
1139 | i++, sfep = dp->d_ops->sf_nextentry(sfp, sfep), |
1140 | oldsfep = dp->d_ops->sf_nextentry(oldsfp, oldsfep)) { | |
2bd0ea18 | 1141 | sfep->namelen = oldsfep->namelen; |
1faa1a81 | 1142 | memcpy(sfep->offset, oldsfep->offset, sizeof(sfep->offset)); |
32181a02 | 1143 | memcpy(sfep->name, oldsfep->name, sfep->namelen); |
ff105f75 DC |
1144 | dp->d_ops->sf_put_ino(sfp, sfep, |
1145 | dp->d_ops->sf_get_ino(oldsfp, oldsfep)); | |
1146 | dp->d_ops->sf_put_ftype(sfep, dp->d_ops->sf_get_ftype(oldsfep)); | |
2bd0ea18 NS |
1147 | } |
1148 | /* | |
1149 | * Clean up the inode. | |
1150 | */ | |
5e656dbb | 1151 | kmem_free(buf); |
2bd0ea18 NS |
1152 | dp->i_d.di_size = newsize; |
1153 | xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA); | |
1154 | } | |
1155 | ||
1156 | /* | |
ff105f75 DC |
1157 | * Convert existing entries from 4-byte inode numbers to 8-byte inode numbers. |
1158 | * The new entry w/ an 8-byte inode number is not there yet; we leave with | |
1159 | * i8count set to 1, but no corresponding 8-byte entry. | |
2bd0ea18 | 1160 | */ |
5e656dbb | 1161 | static void |
2bd0ea18 NS |
1162 | xfs_dir2_sf_toino8( |
1163 | xfs_da_args_t *args) /* operation arguments */ | |
1164 | { | |
1165 | char *buf; /* old dir's buffer */ | |
1166 | xfs_inode_t *dp; /* incore directory inode */ | |
1167 | int i; /* entry index */ | |
2bd0ea18 NS |
1168 | int newsize; /* new inode size */ |
1169 | xfs_dir2_sf_entry_t *oldsfep; /* old sf entry */ | |
a2ceac1f | 1170 | xfs_dir2_sf_hdr_t *oldsfp; /* old sf directory */ |
2bd0ea18 NS |
1171 | int oldsize; /* old inode size */ |
1172 | xfs_dir2_sf_entry_t *sfep; /* new sf entry */ | |
a2ceac1f | 1173 | xfs_dir2_sf_hdr_t *sfp; /* new sf directory */ |
2bd0ea18 | 1174 | |
56b2de80 DC |
1175 | trace_xfs_dir2_sf_toino8(args); |
1176 | ||
2bd0ea18 NS |
1177 | dp = args->dp; |
1178 | ||
1179 | /* | |
1180 | * Copy the old directory to the buffer. | |
1181 | * Then nuke it from the inode, and add the new buffer to the inode. | |
1182 | * Don't want xfs_idata_realloc copying the data here. | |
1183 | */ | |
1184 | oldsize = dp->i_df.if_bytes; | |
1185 | buf = kmem_alloc(oldsize, KM_SLEEP); | |
a2ceac1f DC |
1186 | oldsfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; |
1187 | ASSERT(oldsfp->i8count == 0); | |
32181a02 | 1188 | memcpy(buf, oldsfp, oldsize); |
2bd0ea18 | 1189 | /* |
ff105f75 | 1190 | * Compute the new inode size (nb: entry count + 1 for parent) |
2bd0ea18 | 1191 | */ |
d8bf9c63 | 1192 | newsize = oldsize + (oldsfp->count + 1) * XFS_INO64_DIFF; |
2bd0ea18 NS |
1193 | xfs_idata_realloc(dp, -oldsize, XFS_DATA_FORK); |
1194 | xfs_idata_realloc(dp, newsize, XFS_DATA_FORK); | |
1195 | /* | |
1196 | * Reset our pointers, the data has moved. | |
1197 | */ | |
a2ceac1f DC |
1198 | oldsfp = (xfs_dir2_sf_hdr_t *)buf; |
1199 | sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; | |
2bd0ea18 NS |
1200 | /* |
1201 | * Fill in the new header. | |
1202 | */ | |
a2ceac1f DC |
1203 | sfp->count = oldsfp->count; |
1204 | sfp->i8count = 1; | |
ff105f75 | 1205 | dp->d_ops->sf_put_parent_ino(sfp, dp->d_ops->sf_get_parent_ino(oldsfp)); |
2bd0ea18 NS |
1206 | /* |
1207 | * Copy the entries field by field. | |
1208 | */ | |
5e656dbb BN |
1209 | for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp), |
1210 | oldsfep = xfs_dir2_sf_firstentry(oldsfp); | |
a2ceac1f | 1211 | i < sfp->count; |
ff105f75 DC |
1212 | i++, sfep = dp->d_ops->sf_nextentry(sfp, sfep), |
1213 | oldsfep = dp->d_ops->sf_nextentry(oldsfp, oldsfep)) { | |
2bd0ea18 | 1214 | sfep->namelen = oldsfep->namelen; |
1faa1a81 | 1215 | memcpy(sfep->offset, oldsfep->offset, sizeof(sfep->offset)); |
32181a02 | 1216 | memcpy(sfep->name, oldsfep->name, sfep->namelen); |
ff105f75 DC |
1217 | dp->d_ops->sf_put_ino(sfp, sfep, |
1218 | dp->d_ops->sf_get_ino(oldsfp, oldsfep)); | |
1219 | dp->d_ops->sf_put_ftype(sfep, dp->d_ops->sf_get_ftype(oldsfep)); | |
2bd0ea18 NS |
1220 | } |
1221 | /* | |
1222 | * Clean up the inode. | |
1223 | */ | |
5e656dbb | 1224 | kmem_free(buf); |
2bd0ea18 NS |
1225 | dp->i_d.di_size = newsize; |
1226 | xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA); | |
1227 | } |