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