diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /fs/xfs/xfs_dir2.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'fs/xfs/xfs_dir2.c')
-rw-r--r-- | fs/xfs/xfs_dir2.c | 859 |
1 files changed, 859 insertions, 0 deletions
diff --git a/fs/xfs/xfs_dir2.c b/fs/xfs/xfs_dir2.c new file mode 100644 index 000000000000..49fc0a3695ae --- /dev/null +++ b/fs/xfs/xfs_dir2.c | |||
@@ -0,0 +1,859 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2000-2001 Silicon Graphics, Inc. All Rights Reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of version 2 of the GNU General Public License as | ||
6 | * published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it would be useful, but | ||
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | * | ||
12 | * Further, this software is distributed without any warranty that it is | ||
13 | * free of the rightful claim of any third person regarding infringement | ||
14 | * or the like. Any license provided herein, whether implied or | ||
15 | * otherwise, applies only to this software file. Patent licenses, if | ||
16 | * any, provided herein do not apply to combinations of this program with | ||
17 | * other software, or any other product whatsoever. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License along | ||
20 | * with this program; if not, write the Free Software Foundation, Inc., 59 | ||
21 | * Temple Place - Suite 330, Boston MA 02111-1307, USA. | ||
22 | * | ||
23 | * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, | ||
24 | * Mountain View, CA 94043, or: | ||
25 | * | ||
26 | * http://www.sgi.com | ||
27 | * | ||
28 | * For further information regarding this notice, see: | ||
29 | * | ||
30 | * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ | ||
31 | */ | ||
32 | |||
33 | /* | ||
34 | * XFS v2 directory implmentation. | ||
35 | * Top-level and utility routines. | ||
36 | */ | ||
37 | |||
38 | #include "xfs.h" | ||
39 | |||
40 | #include "xfs_macros.h" | ||
41 | #include "xfs_types.h" | ||
42 | #include "xfs_inum.h" | ||
43 | #include "xfs_log.h" | ||
44 | #include "xfs_trans.h" | ||
45 | #include "xfs_sb.h" | ||
46 | #include "xfs_ag.h" | ||
47 | #include "xfs_dir.h" | ||
48 | #include "xfs_dir2.h" | ||
49 | #include "xfs_dmapi.h" | ||
50 | #include "xfs_mount.h" | ||
51 | #include "xfs_alloc_btree.h" | ||
52 | #include "xfs_bmap_btree.h" | ||
53 | #include "xfs_attr_sf.h" | ||
54 | #include "xfs_dir_sf.h" | ||
55 | #include "xfs_dir2_sf.h" | ||
56 | #include "xfs_dinode.h" | ||
57 | #include "xfs_inode_item.h" | ||
58 | #include "xfs_inode.h" | ||
59 | #include "xfs_bmap.h" | ||
60 | #include "xfs_da_btree.h" | ||
61 | #include "xfs_dir_leaf.h" | ||
62 | #include "xfs_dir2_data.h" | ||
63 | #include "xfs_dir2_leaf.h" | ||
64 | #include "xfs_dir2_block.h" | ||
65 | #include "xfs_dir2_node.h" | ||
66 | #include "xfs_dir2_trace.h" | ||
67 | #include "xfs_error.h" | ||
68 | #include "xfs_bit.h" | ||
69 | |||
70 | /* | ||
71 | * Declarations for interface routines. | ||
72 | */ | ||
73 | static void xfs_dir2_mount(xfs_mount_t *mp); | ||
74 | static int xfs_dir2_isempty(xfs_inode_t *dp); | ||
75 | static int xfs_dir2_init(xfs_trans_t *tp, xfs_inode_t *dp, | ||
76 | xfs_inode_t *pdp); | ||
77 | static int xfs_dir2_createname(xfs_trans_t *tp, xfs_inode_t *dp, | ||
78 | char *name, int namelen, xfs_ino_t inum, | ||
79 | xfs_fsblock_t *first, | ||
80 | xfs_bmap_free_t *flist, xfs_extlen_t total); | ||
81 | static int xfs_dir2_lookup(xfs_trans_t *tp, xfs_inode_t *dp, char *name, | ||
82 | int namelen, xfs_ino_t *inum); | ||
83 | static int xfs_dir2_removename(xfs_trans_t *tp, xfs_inode_t *dp, | ||
84 | char *name, int namelen, xfs_ino_t ino, | ||
85 | xfs_fsblock_t *first, | ||
86 | xfs_bmap_free_t *flist, xfs_extlen_t total); | ||
87 | static int xfs_dir2_getdents(xfs_trans_t *tp, xfs_inode_t *dp, uio_t *uio, | ||
88 | int *eofp); | ||
89 | static int xfs_dir2_replace(xfs_trans_t *tp, xfs_inode_t *dp, char *name, | ||
90 | int namelen, xfs_ino_t inum, | ||
91 | xfs_fsblock_t *first, xfs_bmap_free_t *flist, | ||
92 | xfs_extlen_t total); | ||
93 | static int xfs_dir2_canenter(xfs_trans_t *tp, xfs_inode_t *dp, char *name, | ||
94 | int namelen); | ||
95 | static int xfs_dir2_shortform_validate_ondisk(xfs_mount_t *mp, | ||
96 | xfs_dinode_t *dip); | ||
97 | |||
98 | /* | ||
99 | * Utility routine declarations. | ||
100 | */ | ||
101 | static int xfs_dir2_put_dirent64_direct(xfs_dir2_put_args_t *pa); | ||
102 | static int xfs_dir2_put_dirent64_uio(xfs_dir2_put_args_t *pa); | ||
103 | |||
104 | /* | ||
105 | * Directory operations vector. | ||
106 | */ | ||
107 | xfs_dirops_t xfsv2_dirops = { | ||
108 | .xd_mount = xfs_dir2_mount, | ||
109 | .xd_isempty = xfs_dir2_isempty, | ||
110 | .xd_init = xfs_dir2_init, | ||
111 | .xd_createname = xfs_dir2_createname, | ||
112 | .xd_lookup = xfs_dir2_lookup, | ||
113 | .xd_removename = xfs_dir2_removename, | ||
114 | .xd_getdents = xfs_dir2_getdents, | ||
115 | .xd_replace = xfs_dir2_replace, | ||
116 | .xd_canenter = xfs_dir2_canenter, | ||
117 | .xd_shortform_validate_ondisk = xfs_dir2_shortform_validate_ondisk, | ||
118 | .xd_shortform_to_single = xfs_dir2_sf_to_block, | ||
119 | }; | ||
120 | |||
121 | /* | ||
122 | * Interface routines. | ||
123 | */ | ||
124 | |||
125 | /* | ||
126 | * Initialize directory-related fields in the mount structure. | ||
127 | */ | ||
128 | static void | ||
129 | xfs_dir2_mount( | ||
130 | xfs_mount_t *mp) /* filesystem mount point */ | ||
131 | { | ||
132 | mp->m_dirversion = 2; | ||
133 | ASSERT((1 << (mp->m_sb.sb_blocklog + mp->m_sb.sb_dirblklog)) <= | ||
134 | XFS_MAX_BLOCKSIZE); | ||
135 | mp->m_dirblksize = 1 << (mp->m_sb.sb_blocklog + mp->m_sb.sb_dirblklog); | ||
136 | mp->m_dirblkfsbs = 1 << mp->m_sb.sb_dirblklog; | ||
137 | mp->m_dirdatablk = XFS_DIR2_DB_TO_DA(mp, XFS_DIR2_DATA_FIRSTDB(mp)); | ||
138 | mp->m_dirleafblk = XFS_DIR2_DB_TO_DA(mp, XFS_DIR2_LEAF_FIRSTDB(mp)); | ||
139 | mp->m_dirfreeblk = XFS_DIR2_DB_TO_DA(mp, XFS_DIR2_FREE_FIRSTDB(mp)); | ||
140 | mp->m_attr_node_ents = | ||
141 | (mp->m_sb.sb_blocksize - (uint)sizeof(xfs_da_node_hdr_t)) / | ||
142 | (uint)sizeof(xfs_da_node_entry_t); | ||
143 | mp->m_dir_node_ents = | ||
144 | (mp->m_dirblksize - (uint)sizeof(xfs_da_node_hdr_t)) / | ||
145 | (uint)sizeof(xfs_da_node_entry_t); | ||
146 | mp->m_dir_magicpct = (mp->m_dirblksize * 37) / 100; | ||
147 | } | ||
148 | |||
149 | /* | ||
150 | * Return 1 if directory contains only "." and "..". | ||
151 | */ | ||
152 | static int /* return code */ | ||
153 | xfs_dir2_isempty( | ||
154 | xfs_inode_t *dp) /* incore inode structure */ | ||
155 | { | ||
156 | xfs_dir2_sf_t *sfp; /* shortform directory structure */ | ||
157 | |||
158 | ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR); | ||
159 | /* | ||
160 | * Might happen during shutdown. | ||
161 | */ | ||
162 | if (dp->i_d.di_size == 0) { | ||
163 | return 1; | ||
164 | } | ||
165 | if (dp->i_d.di_size > XFS_IFORK_DSIZE(dp)) | ||
166 | return 0; | ||
167 | sfp = (xfs_dir2_sf_t *)dp->i_df.if_u1.if_data; | ||
168 | return !sfp->hdr.count; | ||
169 | } | ||
170 | |||
171 | /* | ||
172 | * Initialize a directory with its "." and ".." entries. | ||
173 | */ | ||
174 | static int /* error */ | ||
175 | xfs_dir2_init( | ||
176 | xfs_trans_t *tp, /* transaction pointer */ | ||
177 | xfs_inode_t *dp, /* incore directory inode */ | ||
178 | xfs_inode_t *pdp) /* incore parent directory inode */ | ||
179 | { | ||
180 | xfs_da_args_t args; /* operation arguments */ | ||
181 | int error; /* error return value */ | ||
182 | |||
183 | memset((char *)&args, 0, sizeof(args)); | ||
184 | args.dp = dp; | ||
185 | args.trans = tp; | ||
186 | ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR); | ||
187 | if ((error = xfs_dir_ino_validate(tp->t_mountp, pdp->i_ino))) { | ||
188 | return error; | ||
189 | } | ||
190 | return xfs_dir2_sf_create(&args, pdp->i_ino); | ||
191 | } | ||
192 | |||
193 | /* | ||
194 | Enter a name in a directory. | ||
195 | */ | ||
196 | static int /* error */ | ||
197 | xfs_dir2_createname( | ||
198 | xfs_trans_t *tp, /* transaction pointer */ | ||
199 | xfs_inode_t *dp, /* incore directory inode */ | ||
200 | char *name, /* new entry name */ | ||
201 | int namelen, /* new entry name length */ | ||
202 | xfs_ino_t inum, /* new entry inode number */ | ||
203 | xfs_fsblock_t *first, /* bmap's firstblock */ | ||
204 | xfs_bmap_free_t *flist, /* bmap's freeblock list */ | ||
205 | xfs_extlen_t total) /* bmap's total block count */ | ||
206 | { | ||
207 | xfs_da_args_t args; /* operation arguments */ | ||
208 | int rval; /* return value */ | ||
209 | int v; /* type-checking value */ | ||
210 | |||
211 | ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR); | ||
212 | if ((rval = xfs_dir_ino_validate(tp->t_mountp, inum))) { | ||
213 | return rval; | ||
214 | } | ||
215 | XFS_STATS_INC(xs_dir_create); | ||
216 | /* | ||
217 | * Fill in the arg structure for this request. | ||
218 | */ | ||
219 | args.name = name; | ||
220 | args.namelen = namelen; | ||
221 | args.hashval = xfs_da_hashname(name, namelen); | ||
222 | args.inumber = inum; | ||
223 | args.dp = dp; | ||
224 | args.firstblock = first; | ||
225 | args.flist = flist; | ||
226 | args.total = total; | ||
227 | args.whichfork = XFS_DATA_FORK; | ||
228 | args.trans = tp; | ||
229 | args.justcheck = 0; | ||
230 | args.addname = args.oknoent = 1; | ||
231 | /* | ||
232 | * Decide on what work routines to call based on the inode size. | ||
233 | */ | ||
234 | if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) | ||
235 | rval = xfs_dir2_sf_addname(&args); | ||
236 | else if ((rval = xfs_dir2_isblock(tp, dp, &v))) { | ||
237 | return rval; | ||
238 | } else if (v) | ||
239 | rval = xfs_dir2_block_addname(&args); | ||
240 | else if ((rval = xfs_dir2_isleaf(tp, dp, &v))) { | ||
241 | return rval; | ||
242 | } else if (v) | ||
243 | rval = xfs_dir2_leaf_addname(&args); | ||
244 | else | ||
245 | rval = xfs_dir2_node_addname(&args); | ||
246 | return rval; | ||
247 | } | ||
248 | |||
249 | /* | ||
250 | * Lookup a name in a directory, give back the inode number. | ||
251 | */ | ||
252 | static int /* error */ | ||
253 | xfs_dir2_lookup( | ||
254 | xfs_trans_t *tp, /* transaction pointer */ | ||
255 | xfs_inode_t *dp, /* incore directory inode */ | ||
256 | char *name, /* lookup name */ | ||
257 | int namelen, /* lookup name length */ | ||
258 | xfs_ino_t *inum) /* out: inode number */ | ||
259 | { | ||
260 | xfs_da_args_t args; /* operation arguments */ | ||
261 | int rval; /* return value */ | ||
262 | int v; /* type-checking value */ | ||
263 | |||
264 | ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR); | ||
265 | XFS_STATS_INC(xs_dir_lookup); | ||
266 | |||
267 | /* | ||
268 | * Fill in the arg structure for this request. | ||
269 | */ | ||
270 | args.name = name; | ||
271 | args.namelen = namelen; | ||
272 | args.hashval = xfs_da_hashname(name, namelen); | ||
273 | args.inumber = 0; | ||
274 | args.dp = dp; | ||
275 | args.firstblock = NULL; | ||
276 | args.flist = NULL; | ||
277 | args.total = 0; | ||
278 | args.whichfork = XFS_DATA_FORK; | ||
279 | args.trans = tp; | ||
280 | args.justcheck = args.addname = 0; | ||
281 | args.oknoent = 1; | ||
282 | /* | ||
283 | * Decide on what work routines to call based on the inode size. | ||
284 | */ | ||
285 | if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) | ||
286 | rval = xfs_dir2_sf_lookup(&args); | ||
287 | else if ((rval = xfs_dir2_isblock(tp, dp, &v))) { | ||
288 | return rval; | ||
289 | } else if (v) | ||
290 | rval = xfs_dir2_block_lookup(&args); | ||
291 | else if ((rval = xfs_dir2_isleaf(tp, dp, &v))) { | ||
292 | return rval; | ||
293 | } else if (v) | ||
294 | rval = xfs_dir2_leaf_lookup(&args); | ||
295 | else | ||
296 | rval = xfs_dir2_node_lookup(&args); | ||
297 | if (rval == EEXIST) | ||
298 | rval = 0; | ||
299 | if (rval == 0) | ||
300 | *inum = args.inumber; | ||
301 | return rval; | ||
302 | } | ||
303 | |||
304 | /* | ||
305 | * Remove an entry from a directory. | ||
306 | */ | ||
307 | static int /* error */ | ||
308 | xfs_dir2_removename( | ||
309 | xfs_trans_t *tp, /* transaction pointer */ | ||
310 | xfs_inode_t *dp, /* incore directory inode */ | ||
311 | char *name, /* name of entry to remove */ | ||
312 | int namelen, /* name length of entry to remove */ | ||
313 | xfs_ino_t ino, /* inode number of entry to remove */ | ||
314 | xfs_fsblock_t *first, /* bmap's firstblock */ | ||
315 | xfs_bmap_free_t *flist, /* bmap's freeblock list */ | ||
316 | xfs_extlen_t total) /* bmap's total block count */ | ||
317 | { | ||
318 | xfs_da_args_t args; /* operation arguments */ | ||
319 | int rval; /* return value */ | ||
320 | int v; /* type-checking value */ | ||
321 | |||
322 | ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR); | ||
323 | XFS_STATS_INC(xs_dir_remove); | ||
324 | /* | ||
325 | * Fill in the arg structure for this request. | ||
326 | */ | ||
327 | args.name = name; | ||
328 | args.namelen = namelen; | ||
329 | args.hashval = xfs_da_hashname(name, namelen); | ||
330 | args.inumber = ino; | ||
331 | args.dp = dp; | ||
332 | args.firstblock = first; | ||
333 | args.flist = flist; | ||
334 | args.total = total; | ||
335 | args.whichfork = XFS_DATA_FORK; | ||
336 | args.trans = tp; | ||
337 | args.justcheck = args.addname = args.oknoent = 0; | ||
338 | /* | ||
339 | * Decide on what work routines to call based on the inode size. | ||
340 | */ | ||
341 | if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) | ||
342 | rval = xfs_dir2_sf_removename(&args); | ||
343 | else if ((rval = xfs_dir2_isblock(tp, dp, &v))) { | ||
344 | return rval; | ||
345 | } else if (v) | ||
346 | rval = xfs_dir2_block_removename(&args); | ||
347 | else if ((rval = xfs_dir2_isleaf(tp, dp, &v))) { | ||
348 | return rval; | ||
349 | } else if (v) | ||
350 | rval = xfs_dir2_leaf_removename(&args); | ||
351 | else | ||
352 | rval = xfs_dir2_node_removename(&args); | ||
353 | return rval; | ||
354 | } | ||
355 | |||
356 | /* | ||
357 | * Read a directory. | ||
358 | */ | ||
359 | static int /* error */ | ||
360 | xfs_dir2_getdents( | ||
361 | xfs_trans_t *tp, /* transaction pointer */ | ||
362 | xfs_inode_t *dp, /* incore directory inode */ | ||
363 | uio_t *uio, /* caller's buffer control */ | ||
364 | int *eofp) /* out: eof reached */ | ||
365 | { | ||
366 | int alignment; /* alignment required for ABI */ | ||
367 | xfs_dirent_t *dbp; /* malloc'ed buffer */ | ||
368 | xfs_dir2_put_t put; /* entry formatting routine */ | ||
369 | int rval; /* return value */ | ||
370 | int v; /* type-checking value */ | ||
371 | |||
372 | ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR); | ||
373 | XFS_STATS_INC(xs_dir_getdents); | ||
374 | /* | ||
375 | * If our caller has given us a single contiguous aligned memory buffer, | ||
376 | * just work directly within that buffer. If it's in user memory, | ||
377 | * lock it down first. | ||
378 | */ | ||
379 | alignment = sizeof(xfs_off_t) - 1; | ||
380 | if ((uio->uio_iovcnt == 1) && | ||
381 | (((__psint_t)uio->uio_iov[0].iov_base & alignment) == 0) && | ||
382 | ((uio->uio_iov[0].iov_len & alignment) == 0)) { | ||
383 | dbp = NULL; | ||
384 | put = xfs_dir2_put_dirent64_direct; | ||
385 | } else { | ||
386 | dbp = kmem_alloc(sizeof(*dbp) + MAXNAMELEN, KM_SLEEP); | ||
387 | put = xfs_dir2_put_dirent64_uio; | ||
388 | } | ||
389 | |||
390 | *eofp = 0; | ||
391 | /* | ||
392 | * Decide on what work routines to call based on the inode size. | ||
393 | */ | ||
394 | if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) | ||
395 | rval = xfs_dir2_sf_getdents(dp, uio, eofp, dbp, put); | ||
396 | else if ((rval = xfs_dir2_isblock(tp, dp, &v))) { | ||
397 | ; | ||
398 | } else if (v) | ||
399 | rval = xfs_dir2_block_getdents(tp, dp, uio, eofp, dbp, put); | ||
400 | else | ||
401 | rval = xfs_dir2_leaf_getdents(tp, dp, uio, eofp, dbp, put); | ||
402 | if (dbp != NULL) | ||
403 | kmem_free(dbp, sizeof(*dbp) + MAXNAMELEN); | ||
404 | return rval; | ||
405 | } | ||
406 | |||
407 | /* | ||
408 | * Replace the inode number of a directory entry. | ||
409 | */ | ||
410 | static int /* error */ | ||
411 | xfs_dir2_replace( | ||
412 | xfs_trans_t *tp, /* transaction pointer */ | ||
413 | xfs_inode_t *dp, /* incore directory inode */ | ||
414 | char *name, /* name of entry to replace */ | ||
415 | int namelen, /* name length of entry to replace */ | ||
416 | xfs_ino_t inum, /* new inode number */ | ||
417 | xfs_fsblock_t *first, /* bmap's firstblock */ | ||
418 | xfs_bmap_free_t *flist, /* bmap's freeblock list */ | ||
419 | xfs_extlen_t total) /* bmap's total block count */ | ||
420 | { | ||
421 | xfs_da_args_t args; /* operation arguments */ | ||
422 | int rval; /* return value */ | ||
423 | int v; /* type-checking value */ | ||
424 | |||
425 | ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR); | ||
426 | |||
427 | if ((rval = xfs_dir_ino_validate(tp->t_mountp, inum))) { | ||
428 | return rval; | ||
429 | } | ||
430 | /* | ||
431 | * Fill in the arg structure for this request. | ||
432 | */ | ||
433 | args.name = name; | ||
434 | args.namelen = namelen; | ||
435 | args.hashval = xfs_da_hashname(name, namelen); | ||
436 | args.inumber = inum; | ||
437 | args.dp = dp; | ||
438 | args.firstblock = first; | ||
439 | args.flist = flist; | ||
440 | args.total = total; | ||
441 | args.whichfork = XFS_DATA_FORK; | ||
442 | args.trans = tp; | ||
443 | args.justcheck = args.addname = args.oknoent = 0; | ||
444 | /* | ||
445 | * Decide on what work routines to call based on the inode size. | ||
446 | */ | ||
447 | if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) | ||
448 | rval = xfs_dir2_sf_replace(&args); | ||
449 | else if ((rval = xfs_dir2_isblock(tp, dp, &v))) { | ||
450 | return rval; | ||
451 | } else if (v) | ||
452 | rval = xfs_dir2_block_replace(&args); | ||
453 | else if ((rval = xfs_dir2_isleaf(tp, dp, &v))) { | ||
454 | return rval; | ||
455 | } else if (v) | ||
456 | rval = xfs_dir2_leaf_replace(&args); | ||
457 | else | ||
458 | rval = xfs_dir2_node_replace(&args); | ||
459 | return rval; | ||
460 | } | ||
461 | |||
462 | /* | ||
463 | * See if this entry can be added to the directory without allocating space. | ||
464 | */ | ||
465 | static int /* error */ | ||
466 | xfs_dir2_canenter( | ||
467 | xfs_trans_t *tp, /* transaction pointer */ | ||
468 | xfs_inode_t *dp, /* incore directory inode */ | ||
469 | char *name, /* name of entry to add */ | ||
470 | int namelen) /* name length of entry to add */ | ||
471 | { | ||
472 | xfs_da_args_t args; /* operation arguments */ | ||
473 | int rval; /* return value */ | ||
474 | int v; /* type-checking value */ | ||
475 | |||
476 | ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR); | ||
477 | /* | ||
478 | * Fill in the arg structure for this request. | ||
479 | */ | ||
480 | args.name = name; | ||
481 | args.namelen = namelen; | ||
482 | args.hashval = xfs_da_hashname(name, namelen); | ||
483 | args.inumber = 0; | ||
484 | args.dp = dp; | ||
485 | args.firstblock = NULL; | ||
486 | args.flist = NULL; | ||
487 | args.total = 0; | ||
488 | args.whichfork = XFS_DATA_FORK; | ||
489 | args.trans = tp; | ||
490 | args.justcheck = args.addname = args.oknoent = 1; | ||
491 | /* | ||
492 | * Decide on what work routines to call based on the inode size. | ||
493 | */ | ||
494 | if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) | ||
495 | rval = xfs_dir2_sf_addname(&args); | ||
496 | else if ((rval = xfs_dir2_isblock(tp, dp, &v))) { | ||
497 | return rval; | ||
498 | } else if (v) | ||
499 | rval = xfs_dir2_block_addname(&args); | ||
500 | else if ((rval = xfs_dir2_isleaf(tp, dp, &v))) { | ||
501 | return rval; | ||
502 | } else if (v) | ||
503 | rval = xfs_dir2_leaf_addname(&args); | ||
504 | else | ||
505 | rval = xfs_dir2_node_addname(&args); | ||
506 | return rval; | ||
507 | } | ||
508 | |||
509 | /* | ||
510 | * Dummy routine for shortform inode validation. | ||
511 | * Can't really do this. | ||
512 | */ | ||
513 | /* ARGSUSED */ | ||
514 | static int /* error */ | ||
515 | xfs_dir2_shortform_validate_ondisk( | ||
516 | xfs_mount_t *mp, /* filesystem mount point */ | ||
517 | xfs_dinode_t *dip) /* ondisk inode */ | ||
518 | { | ||
519 | return 0; | ||
520 | } | ||
521 | |||
522 | /* | ||
523 | * Utility routines. | ||
524 | */ | ||
525 | |||
526 | /* | ||
527 | * Add a block to the directory. | ||
528 | * This routine is for data and free blocks, not leaf/node blocks | ||
529 | * which are handled by xfs_da_grow_inode. | ||
530 | */ | ||
531 | int /* error */ | ||
532 | xfs_dir2_grow_inode( | ||
533 | xfs_da_args_t *args, /* operation arguments */ | ||
534 | int space, /* v2 dir's space XFS_DIR2_xxx_SPACE */ | ||
535 | xfs_dir2_db_t *dbp) /* out: block number added */ | ||
536 | { | ||
537 | xfs_fileoff_t bno; /* directory offset of new block */ | ||
538 | int count; /* count of filesystem blocks */ | ||
539 | xfs_inode_t *dp; /* incore directory inode */ | ||
540 | int error; /* error return value */ | ||
541 | int got; /* blocks actually mapped */ | ||
542 | int i; /* temp mapping index */ | ||
543 | xfs_bmbt_irec_t map; /* single structure for bmap */ | ||
544 | int mapi; /* mapping index */ | ||
545 | xfs_bmbt_irec_t *mapp; /* bmap mapping structure(s) */ | ||
546 | xfs_mount_t *mp; /* filesystem mount point */ | ||
547 | int nmap; /* number of bmap entries */ | ||
548 | xfs_trans_t *tp; /* transaction pointer */ | ||
549 | |||
550 | xfs_dir2_trace_args_s("grow_inode", args, space); | ||
551 | dp = args->dp; | ||
552 | tp = args->trans; | ||
553 | mp = dp->i_mount; | ||
554 | /* | ||
555 | * Set lowest possible block in the space requested. | ||
556 | */ | ||
557 | bno = XFS_B_TO_FSBT(mp, space * XFS_DIR2_SPACE_SIZE); | ||
558 | count = mp->m_dirblkfsbs; | ||
559 | /* | ||
560 | * Find the first hole for our block. | ||
561 | */ | ||
562 | if ((error = xfs_bmap_first_unused(tp, dp, count, &bno, XFS_DATA_FORK))) { | ||
563 | return error; | ||
564 | } | ||
565 | nmap = 1; | ||
566 | ASSERT(args->firstblock != NULL); | ||
567 | /* | ||
568 | * Try mapping the new block contiguously (one extent). | ||
569 | */ | ||
570 | if ((error = xfs_bmapi(tp, dp, bno, count, | ||
571 | XFS_BMAPI_WRITE|XFS_BMAPI_METADATA|XFS_BMAPI_CONTIG, | ||
572 | args->firstblock, args->total, &map, &nmap, | ||
573 | args->flist))) { | ||
574 | return error; | ||
575 | } | ||
576 | ASSERT(nmap <= 1); | ||
577 | /* | ||
578 | * Got it in 1. | ||
579 | */ | ||
580 | if (nmap == 1) { | ||
581 | mapp = ↦ | ||
582 | mapi = 1; | ||
583 | } | ||
584 | /* | ||
585 | * Didn't work and this is a multiple-fsb directory block. | ||
586 | * Try again with contiguous flag turned on. | ||
587 | */ | ||
588 | else if (nmap == 0 && count > 1) { | ||
589 | xfs_fileoff_t b; /* current file offset */ | ||
590 | |||
591 | /* | ||
592 | * Space for maximum number of mappings. | ||
593 | */ | ||
594 | mapp = kmem_alloc(sizeof(*mapp) * count, KM_SLEEP); | ||
595 | /* | ||
596 | * Iterate until we get to the end of our block. | ||
597 | */ | ||
598 | for (b = bno, mapi = 0; b < bno + count; ) { | ||
599 | int c; /* current fsb count */ | ||
600 | |||
601 | /* | ||
602 | * Can't map more than MAX_NMAP at once. | ||
603 | */ | ||
604 | nmap = MIN(XFS_BMAP_MAX_NMAP, count); | ||
605 | c = (int)(bno + count - b); | ||
606 | if ((error = xfs_bmapi(tp, dp, b, c, | ||
607 | XFS_BMAPI_WRITE|XFS_BMAPI_METADATA, | ||
608 | args->firstblock, args->total, | ||
609 | &mapp[mapi], &nmap, args->flist))) { | ||
610 | kmem_free(mapp, sizeof(*mapp) * count); | ||
611 | return error; | ||
612 | } | ||
613 | if (nmap < 1) | ||
614 | break; | ||
615 | /* | ||
616 | * Add this bunch into our table, go to the next offset. | ||
617 | */ | ||
618 | mapi += nmap; | ||
619 | b = mapp[mapi - 1].br_startoff + | ||
620 | mapp[mapi - 1].br_blockcount; | ||
621 | } | ||
622 | } | ||
623 | /* | ||
624 | * Didn't work. | ||
625 | */ | ||
626 | else { | ||
627 | mapi = 0; | ||
628 | mapp = NULL; | ||
629 | } | ||
630 | /* | ||
631 | * See how many fsb's we got. | ||
632 | */ | ||
633 | for (i = 0, got = 0; i < mapi; i++) | ||
634 | got += mapp[i].br_blockcount; | ||
635 | /* | ||
636 | * Didn't get enough fsb's, or the first/last block's are wrong. | ||
637 | */ | ||
638 | if (got != count || mapp[0].br_startoff != bno || | ||
639 | mapp[mapi - 1].br_startoff + mapp[mapi - 1].br_blockcount != | ||
640 | bno + count) { | ||
641 | if (mapp != &map) | ||
642 | kmem_free(mapp, sizeof(*mapp) * count); | ||
643 | return XFS_ERROR(ENOSPC); | ||
644 | } | ||
645 | /* | ||
646 | * Done with the temporary mapping table. | ||
647 | */ | ||
648 | if (mapp != &map) | ||
649 | kmem_free(mapp, sizeof(*mapp) * count); | ||
650 | *dbp = XFS_DIR2_DA_TO_DB(mp, (xfs_dablk_t)bno); | ||
651 | /* | ||
652 | * Update file's size if this is the data space and it grew. | ||
653 | */ | ||
654 | if (space == XFS_DIR2_DATA_SPACE) { | ||
655 | xfs_fsize_t size; /* directory file (data) size */ | ||
656 | |||
657 | size = XFS_FSB_TO_B(mp, bno + count); | ||
658 | if (size > dp->i_d.di_size) { | ||
659 | dp->i_d.di_size = size; | ||
660 | xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE); | ||
661 | } | ||
662 | } | ||
663 | return 0; | ||
664 | } | ||
665 | |||
666 | /* | ||
667 | * See if the directory is a single-block form directory. | ||
668 | */ | ||
669 | int /* error */ | ||
670 | xfs_dir2_isblock( | ||
671 | xfs_trans_t *tp, /* transaction pointer */ | ||
672 | xfs_inode_t *dp, /* incore directory inode */ | ||
673 | int *vp) /* out: 1 is block, 0 is not block */ | ||
674 | { | ||
675 | xfs_fileoff_t last; /* last file offset */ | ||
676 | xfs_mount_t *mp; /* filesystem mount point */ | ||
677 | int rval; /* return value */ | ||
678 | |||
679 | mp = dp->i_mount; | ||
680 | if ((rval = xfs_bmap_last_offset(tp, dp, &last, XFS_DATA_FORK))) { | ||
681 | return rval; | ||
682 | } | ||
683 | rval = XFS_FSB_TO_B(mp, last) == mp->m_dirblksize; | ||
684 | ASSERT(rval == 0 || dp->i_d.di_size == mp->m_dirblksize); | ||
685 | *vp = rval; | ||
686 | return 0; | ||
687 | } | ||
688 | |||
689 | /* | ||
690 | * See if the directory is a single-leaf form directory. | ||
691 | */ | ||
692 | int /* error */ | ||
693 | xfs_dir2_isleaf( | ||
694 | xfs_trans_t *tp, /* transaction pointer */ | ||
695 | xfs_inode_t *dp, /* incore directory inode */ | ||
696 | int *vp) /* out: 1 is leaf, 0 is not leaf */ | ||
697 | { | ||
698 | xfs_fileoff_t last; /* last file offset */ | ||
699 | xfs_mount_t *mp; /* filesystem mount point */ | ||
700 | int rval; /* return value */ | ||
701 | |||
702 | mp = dp->i_mount; | ||
703 | if ((rval = xfs_bmap_last_offset(tp, dp, &last, XFS_DATA_FORK))) { | ||
704 | return rval; | ||
705 | } | ||
706 | *vp = last == mp->m_dirleafblk + (1 << mp->m_sb.sb_dirblklog); | ||
707 | return 0; | ||
708 | } | ||
709 | |||
710 | /* | ||
711 | * Getdents put routine for 64-bit ABI, direct form. | ||
712 | */ | ||
713 | static int /* error */ | ||
714 | xfs_dir2_put_dirent64_direct( | ||
715 | xfs_dir2_put_args_t *pa) /* argument bundle */ | ||
716 | { | ||
717 | xfs_dirent_t *idbp; /* dirent pointer */ | ||
718 | iovec_t *iovp; /* io vector */ | ||
719 | int namelen; /* entry name length */ | ||
720 | int reclen; /* entry total length */ | ||
721 | uio_t *uio; /* I/O control */ | ||
722 | |||
723 | namelen = pa->namelen; | ||
724 | reclen = DIRENTSIZE(namelen); | ||
725 | uio = pa->uio; | ||
726 | /* | ||
727 | * Won't fit in the remaining space. | ||
728 | */ | ||
729 | if (reclen > uio->uio_resid) { | ||
730 | pa->done = 0; | ||
731 | return 0; | ||
732 | } | ||
733 | iovp = uio->uio_iov; | ||
734 | idbp = (xfs_dirent_t *)iovp->iov_base; | ||
735 | iovp->iov_base = (char *)idbp + reclen; | ||
736 | iovp->iov_len -= reclen; | ||
737 | uio->uio_resid -= reclen; | ||
738 | idbp->d_reclen = reclen; | ||
739 | idbp->d_ino = pa->ino; | ||
740 | idbp->d_off = pa->cook; | ||
741 | idbp->d_name[namelen] = '\0'; | ||
742 | pa->done = 1; | ||
743 | memcpy(idbp->d_name, pa->name, namelen); | ||
744 | return 0; | ||
745 | } | ||
746 | |||
747 | /* | ||
748 | * Getdents put routine for 64-bit ABI, uio form. | ||
749 | */ | ||
750 | static int /* error */ | ||
751 | xfs_dir2_put_dirent64_uio( | ||
752 | xfs_dir2_put_args_t *pa) /* argument bundle */ | ||
753 | { | ||
754 | xfs_dirent_t *idbp; /* dirent pointer */ | ||
755 | int namelen; /* entry name length */ | ||
756 | int reclen; /* entry total length */ | ||
757 | int rval; /* return value */ | ||
758 | uio_t *uio; /* I/O control */ | ||
759 | |||
760 | namelen = pa->namelen; | ||
761 | reclen = DIRENTSIZE(namelen); | ||
762 | uio = pa->uio; | ||
763 | /* | ||
764 | * Won't fit in the remaining space. | ||
765 | */ | ||
766 | if (reclen > uio->uio_resid) { | ||
767 | pa->done = 0; | ||
768 | return 0; | ||
769 | } | ||
770 | idbp = pa->dbp; | ||
771 | idbp->d_reclen = reclen; | ||
772 | idbp->d_ino = pa->ino; | ||
773 | idbp->d_off = pa->cook; | ||
774 | idbp->d_name[namelen] = '\0'; | ||
775 | memcpy(idbp->d_name, pa->name, namelen); | ||
776 | rval = uio_read((caddr_t)idbp, reclen, uio); | ||
777 | pa->done = (rval == 0); | ||
778 | return rval; | ||
779 | } | ||
780 | |||
781 | /* | ||
782 | * Remove the given block from the directory. | ||
783 | * This routine is used for data and free blocks, leaf/node are done | ||
784 | * by xfs_da_shrink_inode. | ||
785 | */ | ||
786 | int | ||
787 | xfs_dir2_shrink_inode( | ||
788 | xfs_da_args_t *args, /* operation arguments */ | ||
789 | xfs_dir2_db_t db, /* directory block number */ | ||
790 | xfs_dabuf_t *bp) /* block's buffer */ | ||
791 | { | ||
792 | xfs_fileoff_t bno; /* directory file offset */ | ||
793 | xfs_dablk_t da; /* directory file offset */ | ||
794 | int done; /* bunmap is finished */ | ||
795 | xfs_inode_t *dp; /* incore directory inode */ | ||
796 | int error; /* error return value */ | ||
797 | xfs_mount_t *mp; /* filesystem mount point */ | ||
798 | xfs_trans_t *tp; /* transaction pointer */ | ||
799 | |||
800 | xfs_dir2_trace_args_db("shrink_inode", args, db, bp); | ||
801 | dp = args->dp; | ||
802 | mp = dp->i_mount; | ||
803 | tp = args->trans; | ||
804 | da = XFS_DIR2_DB_TO_DA(mp, db); | ||
805 | /* | ||
806 | * Unmap the fsblock(s). | ||
807 | */ | ||
808 | if ((error = xfs_bunmapi(tp, dp, da, mp->m_dirblkfsbs, | ||
809 | XFS_BMAPI_METADATA, 0, args->firstblock, args->flist, | ||
810 | &done))) { | ||
811 | /* | ||
812 | * ENOSPC actually can happen if we're in a removename with | ||
813 | * no space reservation, and the resulting block removal | ||
814 | * would cause a bmap btree split or conversion from extents | ||
815 | * to btree. This can only happen for un-fragmented | ||
816 | * directory blocks, since you need to be punching out | ||
817 | * the middle of an extent. | ||
818 | * In this case we need to leave the block in the file, | ||
819 | * and not binval it. | ||
820 | * So the block has to be in a consistent empty state | ||
821 | * and appropriately logged. | ||
822 | * We don't free up the buffer, the caller can tell it | ||
823 | * hasn't happened since it got an error back. | ||
824 | */ | ||
825 | return error; | ||
826 | } | ||
827 | ASSERT(done); | ||
828 | /* | ||
829 | * Invalidate the buffer from the transaction. | ||
830 | */ | ||
831 | xfs_da_binval(tp, bp); | ||
832 | /* | ||
833 | * If it's not a data block, we're done. | ||
834 | */ | ||
835 | if (db >= XFS_DIR2_LEAF_FIRSTDB(mp)) | ||
836 | return 0; | ||
837 | /* | ||
838 | * If the block isn't the last one in the directory, we're done. | ||
839 | */ | ||
840 | if (dp->i_d.di_size > XFS_DIR2_DB_OFF_TO_BYTE(mp, db + 1, 0)) | ||
841 | return 0; | ||
842 | bno = da; | ||
843 | if ((error = xfs_bmap_last_before(tp, dp, &bno, XFS_DATA_FORK))) { | ||
844 | /* | ||
845 | * This can't really happen unless there's kernel corruption. | ||
846 | */ | ||
847 | return error; | ||
848 | } | ||
849 | if (db == mp->m_dirdatablk) | ||
850 | ASSERT(bno == 0); | ||
851 | else | ||
852 | ASSERT(bno > 0); | ||
853 | /* | ||
854 | * Set the size to the new last block. | ||
855 | */ | ||
856 | dp->i_d.di_size = XFS_FSB_TO_B(mp, bno); | ||
857 | xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE); | ||
858 | return 0; | ||
859 | } | ||