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