]>
Commit | Line | Data |
---|---|---|
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 | */ | |
28 | void | |
29 | libxfs_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 | 60 | static int |
2bd0ea18 NS |
61 | libxfs_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 | ||
195 | void | |
db15fab1 | 196 | libxfs_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 | */ | |
263 | int | |
264 | libxfs_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 | */ | |
354 | int | |
355 | libxfs_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 | */ | |
397 | int | |
398 | xfs_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 | */ | |
452 | int | |
453 | xfs_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 | */ | |
514 | int | |
515 | libxfs_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 | ||
533 | int | |
534 | libxfs_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 | */ | |
564 | int | |
565 | libxfs_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 | ||
639 | unsigned int | |
640 | libxfs_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 | */ | |
658 | int | |
659 | libxfs_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 | */ | |
677 | void | |
678 | libxfs_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 | */ | |
692 | void | |
693 | libxfs_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 | */ | |
708 | int | |
709 | libxfs_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 | } |