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