]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - libxfs/util.c
Fix up check of right most branch in a multi-level extent btree
[thirdparty/xfsprogs-dev.git] / libxfs / util.c
CommitLineData
2bd0ea18 1/*
da23017d
NS
2 * Copyright (c) 2000-2005 Silicon Graphics, Inc.
3 * All Rights Reserved.
2bd0ea18 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
NS
7 * published by the Free Software Foundation.
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.
2bd0ea18 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
NS
17 */
18
19#include <xfs.h>
20#include <time.h>
21
2bd0ea18
NS
22/*
23 * Change the requested timestamp in the given inode.
5000d01d 24 *
2bd0ea18
NS
25 * This was once shared with the kernel, but has diverged to the point
26 * where its no longer worth the hassle of maintaining common code.
27 */
28void
29libxfs_ichgtime(xfs_inode_t *ip, int flags)
30{
5000d01d 31 struct timespec tv;
2bd0ea18
NS
32 struct timeval stv;
33
34 gettimeofday(&stv, (struct timezone *)0);
35 tv.tv_sec = stv.tv_sec;
36 tv.tv_nsec = stv.tv_usec * 1000;
37 if (flags & XFS_ICHGTIME_MOD) {
38 ip->i_d.di_mtime.t_sec = (__int32_t)tv.tv_sec;
39 ip->i_d.di_mtime.t_nsec = (__int32_t)tv.tv_nsec;
40 }
41 if (flags & XFS_ICHGTIME_ACC) {
42 ip->i_d.di_atime.t_sec = (__int32_t)tv.tv_sec;
43 ip->i_d.di_atime.t_nsec = (__int32_t)tv.tv_nsec;
44 }
45 if (flags & XFS_ICHGTIME_CHG) {
46 ip->i_d.di_ctime.t_sec = (__int32_t)tv.tv_sec;
47 ip->i_d.di_ctime.t_nsec = (__int32_t)tv.tv_nsec;
48 }
49}
50
51/*
52 * Allocate an inode on disk and return a copy of it's in-core version.
53 * Set mode, nlink, and rdev appropriately within the inode.
54 * The uid and gid for the inode are set according to the contents of
55 * the given cred structure.
56 *
57 * This was once shared with the kernel, but has diverged to the point
58 * where its no longer worth the hassle of maintaining common code.
59 */
9f064b7e 60static int
2bd0ea18
NS
61libxfs_ialloc(
62 xfs_trans_t *tp,
63 xfs_inode_t *pip,
64 mode_t mode,
65 nlink_t nlink,
63899e27 66 xfs_dev_t rdev,
9f064b7e
NS
67 struct cred *cr,
68 struct fsxattr *fsx,
2bd0ea18
NS
69 int okalloc,
70 xfs_buf_t **ialloc_context,
71 boolean_t *call_again,
72 xfs_inode_t **ipp)
73{
74 xfs_ino_t ino;
75 xfs_inode_t *ip;
76 uint flags;
77 int error;
78
79 /*
80 * Call the space management code to pick
81 * the on-disk inode to be allocated.
82 */
83 error = xfs_dialloc(tp, pip ? pip->i_ino : 0, mode, okalloc,
84 ialloc_context, call_again, &ino);
85 if (error != 0)
86 return error;
87 if (*call_again || ino == NULLFSINO) {
88 *ipp = NULL;
89 return 0;
90 }
91 ASSERT(*ialloc_context == NULL);
92
46eca962 93 error = xfs_trans_iget(tp->t_mountp, tp, ino, 0, 0, &ip);
2bd0ea18
NS
94 if (error != 0)
95 return error;
96 ASSERT(ip != NULL);
97
98 ip->i_d.di_mode = (__uint16_t)mode;
99 ip->i_d.di_onlink = 0;
100 ip->i_d.di_nlink = nlink;
101 ASSERT(ip->i_d.di_nlink == nlink);
102 ip->i_d.di_uid = cr->cr_uid;
103 ip->i_d.di_gid = cr->cr_gid;
9f064b7e 104 ip->i_d.di_projid = pip ? 0 : fsx->fsx_projid;
32181a02 105 memset(&(ip->i_d.di_pad[0]), 0, sizeof(ip->i_d.di_pad));
2bd0ea18
NS
106
107 /*
108 * If the superblock version is up to where we support new format
109 * inodes and this is currently an old format inode, then change
110 * the inode version number now. This way we only do the conversion
111 * here rather than here and in the flush/logging code.
112 */
113 if (XFS_SB_VERSION_HASNLINK(&tp->t_mountp->m_sb) &&
114 ip->i_d.di_version == XFS_DINODE_VERSION_1) {
115 ip->i_d.di_version = XFS_DINODE_VERSION_2;
116 /* old link count, projid field, pad field already zeroed */
5000d01d 117 }
2bd0ea18 118
9f064b7e
NS
119 if (pip && (pip->i_d.di_mode & S_ISGID)) {
120 ip->i_d.di_gid = pip->i_d.di_gid;
121 if ((pip->i_d.di_mode & S_ISGID) && (mode & S_IFMT) == S_IFDIR)
122 ip->i_d.di_mode |= S_ISGID;
123 }
124
2bd0ea18
NS
125 ip->i_d.di_size = 0;
126 ip->i_d.di_nextents = 0;
127 ASSERT(ip->i_d.di_nblocks == 0);
128 xfs_ichgtime(ip, XFS_ICHGTIME_CHG|XFS_ICHGTIME_ACC|XFS_ICHGTIME_MOD);
129 /*
130 * di_gen will have been taken care of in xfs_iread.
131 */
9f064b7e 132 ip->i_d.di_extsize = pip ? 0 : fsx->fsx_extsize;
2bd0ea18
NS
133 ip->i_d.di_dmevmask = 0;
134 ip->i_d.di_dmstate = 0;
9f064b7e 135 ip->i_d.di_flags = pip ? 0 : fsx->fsx_xflags;
2bd0ea18 136 flags = XFS_ILOG_CORE;
322f2a29
SL
137 switch (mode & S_IFMT) {
138 case S_IFIFO:
63899e27
NS
139 case S_IFSOCK:
140 /* doesn't make sense to set an rdev for these */
141 rdev = 0;
322f2a29
SL
142 case S_IFCHR:
143 case S_IFBLK:
2bd0ea18 144 ip->i_d.di_format = XFS_DINODE_FMT_DEV;
63899e27 145 ip->i_df.if_u2.if_rdev = rdev;
2bd0ea18
NS
146 flags |= XFS_ILOG_DEV;
147 break;
322f2a29
SL
148 case S_IFREG:
149 case S_IFDIR:
9f064b7e
NS
150 if (pip && (pip->i_d.di_flags & XFS_DIFLAG_ANY)) {
151 uint di_flags = 0;
152
153 if ((mode & S_IFMT) == S_IFDIR) {
154 if (pip->i_d.di_flags & XFS_DIFLAG_RTINHERIT)
155 di_flags |= XFS_DIFLAG_RTINHERIT;
156 if (pip->i_d.di_flags & XFS_DIFLAG_EXTSZINHERIT) {
157 di_flags |= XFS_DIFLAG_EXTSZINHERIT;
158 ip->i_d.di_extsize = pip->i_d.di_extsize;
159 }
160 } else {
161 if (pip->i_d.di_flags & XFS_DIFLAG_RTINHERIT) {
162 di_flags |= XFS_DIFLAG_REALTIME;
163 }
164 if (pip->i_d.di_flags & XFS_DIFLAG_EXTSZINHERIT) {
165 di_flags |= XFS_DIFLAG_EXTSIZE;
166 ip->i_d.di_extsize = pip->i_d.di_extsize;
167 }
168 }
169 if (pip->i_d.di_flags & XFS_DIFLAG_PROJINHERIT)
170 di_flags |= XFS_DIFLAG_PROJINHERIT;
171 ip->i_d.di_flags |= di_flags;
172 }
173 /* FALLTHROUGH */
322f2a29 174 case S_IFLNK:
2bd0ea18
NS
175 ip->i_d.di_format = XFS_DINODE_FMT_EXTENTS;
176 ip->i_df.if_flags = XFS_IFEXTENTS;
177 ip->i_df.if_bytes = ip->i_df.if_real_bytes = 0;
178 ip->i_df.if_u1.if_extents = NULL;
179 break;
180 default:
181 ASSERT(0);
182 }
183 /* Attribute fork settings for new inode. */
184 ip->i_d.di_aformat = XFS_DINODE_FMT_EXTENTS;
185 ip->i_d.di_anextents = 0;
186
187 /*
188 * Log the new values stuffed into the inode.
189 */
190 xfs_trans_log_inode(tp, ip, flags);
191 *ipp = ip;
192 return 0;
193}
194
195void
db15fab1 196libxfs_iprint(xfs_inode_t *ip)
2bd0ea18
NS
197{
198 xfs_dinode_core_t *dip;
db15fab1
NS
199 xfs_bmbt_rec_t *ep;
200 xfs_extnum_t i;
201 xfs_extnum_t nextents;
2bd0ea18 202
5b64e00a 203 printf("Inode %lx\n", (unsigned long)ip);
5b64e00a 204 printf(" i_ino %llx\n", (unsigned long long)ip->i_ino);
2bd0ea18
NS
205
206 if (ip->i_df.if_flags & XFS_IFEXTENTS)
207 printf("EXTENTS ");
208 printf("\n");
209 printf(" i_df.if_bytes %d\n", ip->i_df.if_bytes);
5b64e00a
NS
210 printf(" i_df.if_u1.if_extents/if_data %lx\n",
211 (unsigned long)ip->i_df.if_u1.if_extents);
2bd0ea18
NS
212 if (ip->i_df.if_flags & XFS_IFEXTENTS) {
213 nextents = ip->i_df.if_bytes / (uint)sizeof(*ep);
214 for (ep = ip->i_df.if_u1.if_extents, i = 0; i < nextents; i++, ep++) {
5000d01d 215 xfs_bmbt_irec_t rec;
2bd0ea18
NS
216
217 xfs_bmbt_get_all(ep, &rec);
5b64e00a
NS
218 printf("\t%d: startoff %llu, startblock 0x%llx,"
219 " blockcount %llu, state %d\n",
220 i, (unsigned long long)rec.br_startoff,
221 (unsigned long long)rec.br_startblock,
222 (unsigned long long)rec.br_blockcount,
2bd0ea18
NS
223 (int)rec.br_state);
224 }
225 }
5b64e00a 226 printf(" i_df.if_broot %lx\n", (unsigned long)ip->i_df.if_broot);
2bd0ea18
NS
227 printf(" i_df.if_broot_bytes %x\n", ip->i_df.if_broot_bytes);
228
229 dip = &(ip->i_d);
230 printf("\nOn disk portion\n");
231 printf(" di_magic %x\n", dip->di_magic);
232 printf(" di_mode %o\n", dip->di_mode);
233 printf(" di_version %x\n", (uint)dip->di_version);
234 switch (ip->i_d.di_format) {
235 case XFS_DINODE_FMT_LOCAL:
236 printf(" Inline inode\n");
237 break;
238 case XFS_DINODE_FMT_EXTENTS:
239 printf(" Extents inode\n");
240 break;
241 case XFS_DINODE_FMT_BTREE:
242 printf(" B-tree inode\n");
243 break;
244 default:
245 printf(" Other inode\n");
246 break;
247 }
248 printf(" di_nlink %x\n", dip->di_nlink);
249 printf(" di_uid %d\n", dip->di_uid);
250 printf(" di_gid %d\n", dip->di_gid);
251 printf(" di_nextents %d\n", dip->di_nextents);
5b64e00a 252 printf(" di_size %llu\n", (unsigned long long)dip->di_size);
2bd0ea18
NS
253 printf(" di_gen %x\n", dip->di_gen);
254 printf(" di_extsize %d\n", dip->di_extsize);
255 printf(" di_flags %x\n", dip->di_flags);
5b64e00a 256 printf(" di_nblocks %llu\n", (unsigned long long)dip->di_nblocks);
2bd0ea18
NS
257}
258
259/*
260 * Writes a modified inode's changes out to the inode's on disk home.
261 * Originally based on xfs_iflush_int() from xfs_inode.c in the kernel.
262 */
263int
264libxfs_iflush_int(xfs_inode_t *ip, xfs_buf_t *bp)
265{
266 xfs_inode_log_item_t *iip;
267 xfs_dinode_t *dip;
268 xfs_mount_t *mp;
269
270 ASSERT(XFS_BUF_FSPRIVATE(bp, void *) != NULL);
271 ASSERT(ip->i_d.di_format != XFS_DINODE_FMT_BTREE ||
272 ip->i_d.di_nextents > ip->i_df.if_ext_max);
273
274 iip = ip->i_itemp;
275 mp = ip->i_mount;
276
277 /* set *dip = inode's place in the buffer */
278 dip = (xfs_dinode_t *)xfs_buf_offset(bp, ip->i_boffset);
279
280#ifdef DEBUG
281 ASSERT(ip->i_d.di_magic == XFS_DINODE_MAGIC);
322f2a29 282 if ((ip->i_d.di_mode & S_IFMT) == S_IFREG) {
2bd0ea18
NS
283 ASSERT( (ip->i_d.di_format == XFS_DINODE_FMT_EXTENTS) ||
284 (ip->i_d.di_format == XFS_DINODE_FMT_BTREE) );
285 }
322f2a29 286 else if ((ip->i_d.di_mode & S_IFMT) == S_IFDIR) {
2bd0ea18
NS
287 ASSERT( (ip->i_d.di_format == XFS_DINODE_FMT_EXTENTS) ||
288 (ip->i_d.di_format == XFS_DINODE_FMT_BTREE) ||
289 (ip->i_d.di_format == XFS_DINODE_FMT_LOCAL) );
290 }
291 ASSERT(ip->i_d.di_nextents+ip->i_d.di_anextents <= ip->i_d.di_nblocks);
292 ASSERT(ip->i_d.di_forkoff <= mp->m_sb.sb_inodesize);
293#endif
294
295 /*
296 * Copy the dirty parts of the inode into the on-disk
297 * inode. We always copy out the core of the inode,
298 * because if the inode is dirty at all the core must
299 * be.
300 */
46eca962
NS
301 xfs_xlate_dinode_core((xfs_caddr_t)&(dip->di_core), &(ip->i_d), -1);
302
2bd0ea18
NS
303 /*
304 * If this is really an old format inode and the superblock version
305 * has not been updated to support only new format inodes, then
306 * convert back to the old inode format. If the superblock version
307 * has been updated, then make the conversion permanent.
308 */
309 ASSERT(ip->i_d.di_version == XFS_DINODE_VERSION_1 ||
310 XFS_SB_VERSION_HASNLINK(&mp->m_sb));
311 if (ip->i_d.di_version == XFS_DINODE_VERSION_1) {
312 if (!XFS_SB_VERSION_HASNLINK(&mp->m_sb)) {
313 /*
314 * Convert it back.
315 */
316 ASSERT(ip->i_d.di_nlink <= XFS_MAXLINK_1);
317 INT_SET(dip->di_core.di_onlink, ARCH_CONVERT,
318 ip->i_d.di_nlink);
319 } else {
320 /*
321 * The superblock version has already been bumped,
322 * so just make the conversion to the new inode
323 * format permanent.
324 */
325 ip->i_d.di_version = XFS_DINODE_VERSION_2;
326 INT_SET(dip->di_core.di_version, ARCH_CONVERT,
327 XFS_DINODE_VERSION_2);
328 ip->i_d.di_onlink = 0;
46eca962 329 dip->di_core.di_onlink = 0;
32181a02
NS
330 memset(&(ip->i_d.di_pad[0]), 0, sizeof(ip->i_d.di_pad));
331 memset(&(dip->di_core.di_pad[0]), 0,
2bd0ea18
NS
332 sizeof(dip->di_core.di_pad));
333 ASSERT(ip->i_d.di_projid == 0);
334 }
335 }
336
337 if (xfs_iflush_fork(ip, dip, iip, XFS_DATA_FORK, bp) == EFSCORRUPTED)
338 return EFSCORRUPTED;
339 if (XFS_IFORK_Q(ip)) {
340 /* The only error from xfs_iflush_fork is on the data fork. */
341 xfs_iflush_fork(ip, dip, iip, XFS_ATTR_FORK, bp);
342 }
343
344 return 0;
345}
346
347/*
348 * Given a block number in a fork, return the next valid block number
349 * (not a hole).
350 * If this is the last block number then NULLFILEOFF is returned.
351 *
352 * This was originally in the kernel, but only used in xfs_repair.
353 */
354int
355libxfs_bmap_next_offset(
356 xfs_trans_t *tp, /* transaction pointer */
357 xfs_inode_t *ip, /* incore inode */
358 xfs_fileoff_t *bnop, /* current block */
359 int whichfork) /* data or attr fork */
360{
361 xfs_fileoff_t bno; /* current block */
362 int eof; /* hit end of file */
363 int error; /* error return value */
5000d01d 364 xfs_bmbt_irec_t got; /* current extent value */
2bd0ea18
NS
365 xfs_ifork_t *ifp; /* inode fork pointer */
366 xfs_extnum_t lastx; /* last extent used */
5000d01d 367 xfs_bmbt_irec_t prev; /* previous extent value */
2bd0ea18
NS
368
369 if (XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE &&
370 XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS &&
371 XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_LOCAL)
372 return XFS_ERROR(EIO);
373 if (XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_LOCAL) {
374 *bnop = NULLFILEOFF;
375 return 0;
376 }
377 ifp = XFS_IFORK_PTR(ip, whichfork);
378 if (!(ifp->if_flags & XFS_IFEXTENTS) &&
379 (error = xfs_iread_extents(tp, ip, whichfork)))
380 return error;
381 bno = *bnop + 1;
382 xfs_bmap_search_extents(ip, bno, whichfork, &eof, &lastx, &got, &prev);
383 if (eof)
384 *bnop = NULLFILEOFF;
385 else
386 *bnop = got.br_startoff < bno ? bno : got.br_startoff;
387 return 0;
388}
389
390/*
391 * Like xfs_dir_removename, but only for removing entries with
392 * (name, hashvalue) pairs that may not be consistent (hashvalue
393 * may not be correctly set for the name).
5000d01d 394 *
2bd0ea18
NS
395 * This was originally in the kernel, but only used in xfs_repair.
396 */
397int
398xfs_dir_bogus_removename(xfs_trans_t *trans, xfs_inode_t *dp, char *name,
399 xfs_fsblock_t *firstblock, xfs_bmap_free_t *flist,
400 xfs_extlen_t total, xfs_dahash_t hashval, int namelen)
401{
402 xfs_da_args_t args;
403 int count, totallen, newsize, retval;
404
322f2a29 405 ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
2bd0ea18
NS
406 if (namelen >= MAXNAMELEN) {
407 return EINVAL;
408 }
409
410 /*
411 * Fill in the arg structure for this request.
412 */
413 args.name = name;
414 args.namelen = namelen;
415 args.hashval = hashval;
416 args.inumber = 0;
417 args.dp = dp;
418 args.firstblock = firstblock;
419 args.flist = flist;
420 args.total = total;
421 args.whichfork = XFS_DATA_FORK;
422 args.trans = trans;
423 args.justcheck = args.addname = 0;
424 args.oknoent = 1;
425
426 /*
427 * Decide on what work routines to call based on the inode size.
428 */
429 if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL) {
430 retval = xfs_dir_shortform_removename(&args);
431 } else if (xfs_bmap_one_block(dp, XFS_DATA_FORK)) {
432 retval = xfs_dir_leaf_removename(&args, &count, &totallen);
433 if (retval == 0) {
434 newsize = XFS_DIR_SF_ALLFIT(count, totallen);
435 if (newsize <= XFS_IFORK_DSIZE(dp)) {
436 retval = xfs_dir_leaf_to_shortform(&args);
437 }
438 }
439 } else {
440 retval = xfs_dir_node_removename(&args);
441 }
442 return(retval);
443}
444
445/*
446 * Like xfs_dir_removename, but only for removing entries with
447 * (name, hashvalue) pairs that may not be consistent (hashvalue
448 * may not be correctly set for the name).
5000d01d 449 *
2bd0ea18
NS
450 * This was originally in the kernel, but only used in xfs_repair.
451 */
452int
453xfs_dir2_bogus_removename(
454 xfs_trans_t *tp, /* transaction pointer */
455 xfs_inode_t *dp, /* incore directory inode */
456 char *name, /* name of entry to remove */
457 xfs_fsblock_t *first, /* bmap's firstblock */
5000d01d 458 xfs_bmap_free_t *flist, /* bmap's freeblock list */
2bd0ea18
NS
459 xfs_extlen_t total, /* bmap's total block count */
460 xfs_dahash_t hash, /* name's real hash value */
461 int namelen) /* entry's name length */
462{
463 xfs_da_args_t args; /* operation arguments */
464 int rval; /* return value */
465 int v; /* type-checking value */
466
322f2a29 467 ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
2bd0ea18
NS
468 if (namelen >= MAXNAMELEN)
469 return EINVAL;
470
471 /*
472 * Fill in the arg structure for this request.
473 */
474 args.name = name;
475 args.namelen = namelen;
476 args.hashval = hash;
477 args.inumber = 0;
478 args.dp = dp;
479 args.firstblock = first;
480 args.flist = flist;
481 args.total = total;
482 args.whichfork = XFS_DATA_FORK;
483 args.trans = tp;
484 args.justcheck = args.addname = 0;
485 args.oknoent = 1;
486
487 /*
488 * Decide on what work routines to call based on the inode size.
489 */
490 if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
491 rval = xfs_dir2_sf_removename(&args);
0e266570 492 else if ((rval = xfs_dir2_isblock(tp, dp, &v)))
2bd0ea18
NS
493 return rval;
494 else if (v)
495 rval = xfs_dir2_block_removename(&args);
0e266570 496 else if ((rval = xfs_dir2_isleaf(tp, dp, &v)))
2bd0ea18
NS
497 return rval;
498 else if (v)
499 rval = xfs_dir2_leaf_removename(&args);
500 else
501 rval = xfs_dir2_node_removename(&args);
502 return rval;
503}
504
505/*
506 * Utility routine common used to apply a delta to a field in the
507 * in-core superblock.
508 * Switch on the field indicated and apply the delta to that field.
509 * Fields are not allowed to dip below zero, so if the delta would
510 * do this do not apply it and return EINVAL.
511 *
512 * Originally derived from xfs_mod_incore_sb().
513 */
514int
515libxfs_mod_incore_sb(xfs_mount_t *mp, xfs_sb_field_t field, int delta, int rsvd)
516{
517 long long lcounter; /* long counter for 64 bit fields */
518
519 switch (field) {
520 case XFS_SBS_FDBLOCKS:
521 lcounter = (long long)mp->m_sb.sb_fdblocks;
522 lcounter += delta;
523 if (lcounter < 0)
524 return (XFS_ERROR(ENOSPC));
525 mp->m_sb.sb_fdblocks = lcounter;
526 break;
527 default:
528 ASSERT(0);
529 }
530 return 0;
531}
532
533int
534libxfs_bmap_finish(
535 xfs_trans_t **tp,
5000d01d 536 xfs_bmap_free_t *flist,
2bd0ea18
NS
537 xfs_fsblock_t firstblock,
538 int *committed)
539{
540 xfs_bmap_free_item_t *free; /* free extent list item */
541 xfs_bmap_free_item_t *next; /* next item on free list */
542 int error;
2bd0ea18
NS
543
544 if (flist->xbf_count == 0) {
545 *committed = 0;
546 return 0;
547 }
548
549 for (free = flist->xbf_first; free != NULL; free = next) {
550 next = free->xbfi_next;
0e266570
NS
551 if ((error = xfs_free_extent(*tp, free->xbfi_startblock,
552 free->xbfi_blockcount)))
2bd0ea18
NS
553 return error;
554 xfs_bmap_del_free(flist, NULL, free);
555 }
57c9fccb 556 *committed = 0;
2bd0ea18
NS
557 return 0;
558}
559
560/*
561 * This routine allocates disk space for the given file.
562 * Originally derived from xfs_alloc_file_space().
563 */
564int
565libxfs_alloc_file_space(
566 xfs_inode_t *ip,
567 xfs_off_t offset,
568 xfs_off_t len,
569 int alloc_type,
570 int attr_flags)
571{
572 xfs_mount_t *mp;
573 xfs_off_t count;
574 xfs_filblks_t datablocks;
575 xfs_filblks_t allocated_fsb;
576 xfs_filblks_t allocatesize_fsb;
577 xfs_fsblock_t firstfsb;
5000d01d
SL
578 xfs_bmap_free_t free_list;
579 xfs_bmbt_irec_t *imapp;
580 xfs_bmbt_irec_t imaps[1];
2bd0ea18
NS
581 int reccount;
582 uint resblks;
583 xfs_fileoff_t startoffset_fsb;
584 xfs_trans_t *tp;
585 int xfs_bmapi_flags;
586 int committed;
587 int error;
588
589 if (len <= 0)
590 return EINVAL;
591
592 count = len;
593 error = 0;
594 imapp = &imaps[0];
595 reccount = 1;
596 xfs_bmapi_flags = XFS_BMAPI_WRITE | (alloc_type ? XFS_BMAPI_PREALLOC : 0);
597 mp = ip->i_mount;
598 startoffset_fsb = XFS_B_TO_FSBT(mp, offset);
599 allocatesize_fsb = XFS_B_TO_FSB(mp, count);
600
601 /* allocate file space until done or until there is an error */
602 while (allocatesize_fsb && !error) {
603 datablocks = allocatesize_fsb;
604
605 tp = xfs_trans_alloc(mp, XFS_TRANS_DIOSTRAT);
606 resblks = (uint)XFS_DIOSTRAT_SPACE_RES(mp, datablocks);
607 error = xfs_trans_reserve(tp, resblks, 0, 0, 0, 0);
608 if (error)
609 break;
610 xfs_trans_ijoin(tp, ip, 0);
611 xfs_trans_ihold(tp, ip);
612
613 XFS_BMAP_INIT(&free_list, &firstfsb);
614 error = xfs_bmapi(tp, ip, startoffset_fsb, allocatesize_fsb,
615 xfs_bmapi_flags, &firstfsb, 0, imapp,
616 &reccount, &free_list);
617 if (error)
618 break;
619
620 /* complete the transaction */
621 error = xfs_bmap_finish(&tp, &free_list, firstfsb, &committed);
622 if (error)
623 break;
624
625 error = xfs_trans_commit(tp, 0, NULL);
626 if (error)
627 break;
628
629 allocated_fsb = imapp->br_blockcount;
630 if (reccount == 0)
631 return ENOSPC;
632
633 startoffset_fsb += allocated_fsb;
634 allocatesize_fsb -= allocated_fsb;
635 }
636 return error;
637}
638
639unsigned int
640libxfs_log2_roundup(unsigned int i)
641{
642 unsigned int rval;
643
644 for (rval = 0; rval < NBBY * sizeof(i); rval++) {
645 if ((1 << rval) >= i)
646 break;
647 }
648 return rval;
649}
650
651/*
652 * Get a buffer for the dir/attr block, fill in the contents.
653 * Don't check magic number, the caller will (it's xfs_repair).
5000d01d 654 *
2bd0ea18
NS
655 * Originally from xfs_da_btree.c in the kernel, but only used
656 * in userspace so it now resides here.
657 */
658int
659libxfs_da_read_bufr(
660 xfs_trans_t *trans,
661 xfs_inode_t *dp,
662 xfs_dablk_t bno,
a981f202 663 xfs_daddr_t mappedbno,
2bd0ea18
NS
664 xfs_dabuf_t **bpp,
665 int whichfork)
666{
667 return libxfs_da_do_buf(trans, dp, bno, &mappedbno, bpp, whichfork, 2,
668 (inst_t *)__return_address);
669}
670
671/*
672 * Hold dabuf at transaction commit.
5000d01d 673 *
2bd0ea18
NS
674 * Originally from xfs_da_btree.c in the kernel, but only used
675 * in userspace so it now resides here.
676 */
677void
678libxfs_da_bhold(xfs_trans_t *tp, xfs_dabuf_t *dabuf)
679{
680 int i;
681
682 for (i = 0; i < dabuf->nbuf; i++)
683 xfs_trans_bhold(tp, dabuf->bps[i]);
684}
685
686/*
687 * Join dabuf to transaction.
5000d01d 688 *
2bd0ea18
NS
689 * Originally from xfs_da_btree.c in the kernel, but only used
690 * in userspace so it now resides here.
691 */
692void
693libxfs_da_bjoin(xfs_trans_t *tp, xfs_dabuf_t *dabuf)
694{
695 int i;
696
697 for (i = 0; i < dabuf->nbuf; i++)
698 xfs_trans_bjoin(tp, dabuf->bps[i]);
699}
9f064b7e
NS
700
701/*
702 * Wrapper around call to libxfs_ialloc. Takes care of committing and
703 * allocating a new transaction as needed.
704 *
705 * Originally there were two copies of this code - one in mkfs, the
706 * other in repair - now there is just the one.
707 */
708int
709libxfs_inode_alloc(
710 xfs_trans_t **tp,
711 xfs_inode_t *pip,
712 mode_t mode,
713 nlink_t nlink,
714 xfs_dev_t rdev,
715 struct cred *cr,
716 struct fsxattr *fsx,
717 xfs_inode_t **ipp)
718{
719 boolean_t call_again;
720 int i;
721 xfs_buf_t *ialloc_context;
722 xfs_inode_t *ip;
723 xfs_trans_t *ntp;
724 int error;
725
726 call_again = B_FALSE;
727 ialloc_context = (xfs_buf_t *)0;
728 error = libxfs_ialloc(*tp, pip, mode, nlink, rdev, cr, fsx,
729 1, &ialloc_context, &call_again, &ip);
730 if (error)
731 return error;
732
733 if (call_again) {
734 xfs_trans_bhold(*tp, ialloc_context);
735 ntp = xfs_trans_dup(*tp);
736 xfs_trans_commit(*tp, 0, NULL);
737 *tp = ntp;
738 if ((i = xfs_trans_reserve(*tp, 0, 0, 0, 0, 0))) {
739 fprintf(stderr, _("%s: cannot reserve space: %s\n"),
740 progname, strerror(errno));
741 exit(1);
742 }
743 xfs_trans_bjoin(*tp, ialloc_context);
744 error = libxfs_ialloc(*tp, pip, mode, nlink, rdev, cr,
745 fsx, 1, &ialloc_context,
746 &call_again, &ip);
747 if (!ip)
748 error = ENOSPC;
749 if (error)
750 return error;
751 }
752 if (!ip)
753 error = ENOSPC;
754
755 *ipp = ip;
756 return error;
757}