patch-2_6_7-vs1_9_1_12
[linux-2.6.git] / fs / xfs / xfs_fsops.c
1 /*
2  * Copyright (c) 2000-2002 Silicon Graphics, Inc.  All Rights Reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of version 2 of the GNU General Public License as
6  * published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it would be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11  *
12  * Further, this software is distributed without any warranty that it is
13  * free of the rightful claim of any third person regarding infringement
14  * or the like.  Any license provided herein, whether implied or
15  * otherwise, applies only to this software file.  Patent licenses, if
16  * any, provided herein do not apply to combinations of this program with
17  * other software, or any other product whatsoever.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write the Free Software Foundation, Inc., 59
21  * Temple Place - Suite 330, Boston MA 02111-1307, USA.
22  *
23  * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
24  * Mountain View, CA  94043, or:
25  *
26  * http://www.sgi.com
27  *
28  * For further information regarding this notice, see:
29  *
30  * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
31  */
32
33 #include "xfs.h"
34 #include "xfs_macros.h"
35 #include "xfs_types.h"
36 #include "xfs_inum.h"
37 #include "xfs_log.h"
38 #include "xfs_trans.h"
39 #include "xfs_sb.h"
40 #include "xfs_dir.h"
41 #include "xfs_dmapi.h"
42 #include "xfs_mount.h"
43 #include "xfs_ag.h"
44 #include "xfs_alloc_btree.h"
45 #include "xfs_bmap_btree.h"
46 #include "xfs_ialloc_btree.h"
47 #include "xfs_btree.h"
48 #include "xfs_error.h"
49 #include "xfs_alloc.h"
50 #include "xfs_ialloc.h"
51 #include "xfs_fsops.h"
52 #include "xfs_itable.h"
53 #include "xfs_rw.h"
54 #include "xfs_refcache.h"
55 #include "xfs_trans_space.h"
56 #include "xfs_rtalloc.h"
57 #include "xfs_dir2.h"
58 #include "xfs_attr_sf.h"
59 #include "xfs_dir_sf.h"
60 #include "xfs_dir2_sf.h"
61 #include "xfs_dinode.h"
62 #include "xfs_inode.h"
63 #include "xfs_inode_item.h"
64
65 /*
66  * File system operations
67  */
68
69 int
70 xfs_fs_geometry(
71         xfs_mount_t             *mp,
72         xfs_fsop_geom_t         *geo,
73         int                     new_version)
74 {
75         geo->blocksize = mp->m_sb.sb_blocksize;
76         geo->rtextsize = mp->m_sb.sb_rextsize;
77         geo->agblocks = mp->m_sb.sb_agblocks;
78         geo->agcount = mp->m_sb.sb_agcount;
79         geo->logblocks = mp->m_sb.sb_logblocks;
80         geo->sectsize = mp->m_sb.sb_sectsize;
81         geo->inodesize = mp->m_sb.sb_inodesize;
82         geo->imaxpct = mp->m_sb.sb_imax_pct;
83         geo->datablocks = mp->m_sb.sb_dblocks;
84         geo->rtblocks = mp->m_sb.sb_rblocks;
85         geo->rtextents = mp->m_sb.sb_rextents;
86         geo->logstart = mp->m_sb.sb_logstart;
87         ASSERT(sizeof(geo->uuid)==sizeof(mp->m_sb.sb_uuid));
88         memcpy(geo->uuid, &mp->m_sb.sb_uuid, sizeof(mp->m_sb.sb_uuid));
89         if (new_version >= 2) {
90                 geo->sunit = mp->m_sb.sb_unit;
91                 geo->swidth = mp->m_sb.sb_width;
92         }
93         if (new_version >= 3) {
94                 geo->version = XFS_FSOP_GEOM_VERSION;
95                 geo->flags =
96                         (XFS_SB_VERSION_HASATTR(&mp->m_sb) ?
97                                 XFS_FSOP_GEOM_FLAGS_ATTR : 0) |
98                         (XFS_SB_VERSION_HASNLINK(&mp->m_sb) ?
99                                 XFS_FSOP_GEOM_FLAGS_NLINK : 0) |
100                         (XFS_SB_VERSION_HASQUOTA(&mp->m_sb) ?
101                                 XFS_FSOP_GEOM_FLAGS_QUOTA : 0) |
102                         (XFS_SB_VERSION_HASALIGN(&mp->m_sb) ?
103                                 XFS_FSOP_GEOM_FLAGS_IALIGN : 0) |
104                         (XFS_SB_VERSION_HASDALIGN(&mp->m_sb) ?
105                                 XFS_FSOP_GEOM_FLAGS_DALIGN : 0) |
106                         (XFS_SB_VERSION_HASSHARED(&mp->m_sb) ?
107                                 XFS_FSOP_GEOM_FLAGS_SHARED : 0) |
108                         (XFS_SB_VERSION_HASEXTFLGBIT(&mp->m_sb) ?
109                                 XFS_FSOP_GEOM_FLAGS_EXTFLG : 0) |
110                         (XFS_SB_VERSION_HASDIRV2(&mp->m_sb) ?
111                                 XFS_FSOP_GEOM_FLAGS_DIRV2 : 0) |
112                         (XFS_SB_VERSION_HASSECTOR(&mp->m_sb) ?
113                                 XFS_FSOP_GEOM_FLAGS_SECTOR : 0);
114                 geo->logsectsize = XFS_SB_VERSION_HASSECTOR(&mp->m_sb) ?
115                                 mp->m_sb.sb_logsectsize : BBSIZE;
116                 geo->rtsectsize = mp->m_sb.sb_blocksize;
117                 geo->dirblocksize = mp->m_dirblksize;
118         }
119         if (new_version >= 4) {
120                 geo->flags |=
121                         (XFS_SB_VERSION_HASLOGV2(&mp->m_sb) ?
122                                 XFS_FSOP_GEOM_FLAGS_LOGV2 : 0);
123                 geo->logsunit = mp->m_sb.sb_logsunit;
124         }
125         return 0;
126 }
127
128 static int
129 xfs_growfs_data_private(
130         xfs_mount_t             *mp,            /* mount point for filesystem */
131         xfs_growfs_data_t       *in)            /* growfs data input struct */
132 {
133         xfs_agf_t               *agf;
134         xfs_agi_t               *agi;
135         xfs_agnumber_t          agno;
136         xfs_extlen_t            agsize;
137         xfs_extlen_t            tmpsize;
138         xfs_alloc_rec_t         *arec;
139         xfs_btree_sblock_t      *block;
140         xfs_buf_t               *bp;
141         int                     bucket;
142         int                     dpct;
143         int                     error;
144         xfs_agnumber_t          nagcount;
145         xfs_rfsblock_t          nb, nb_mod;
146         xfs_rfsblock_t          new;
147         xfs_rfsblock_t          nfree;
148         xfs_agnumber_t          oagcount;
149         int                     pct;
150         xfs_sb_t                *sbp;
151         xfs_trans_t             *tp;
152
153         nb = in->newblocks;
154         pct = in->imaxpct;
155         if (nb < mp->m_sb.sb_dblocks || pct < 0 || pct > 100)
156                 return XFS_ERROR(EINVAL);
157         dpct = pct - mp->m_sb.sb_imax_pct;
158         error = xfs_read_buf(mp, mp->m_ddev_targp,
159                         XFS_FSB_TO_BB(mp, nb) - XFS_FSS_TO_BB(mp, 1),
160                         XFS_FSS_TO_BB(mp, 1), 0, &bp);
161         if (error)
162                 return error;
163         ASSERT(bp);
164         xfs_buf_relse(bp);
165
166         new = nb;       /* use new as a temporary here */
167         nb_mod = do_div(new, mp->m_sb.sb_agblocks);
168         nagcount = new + (nb_mod != 0);
169         if (nb_mod && nb_mod < XFS_MIN_AG_BLOCKS) {
170                 nagcount--;
171                 nb = nagcount * mp->m_sb.sb_agblocks;
172                 if (nb < mp->m_sb.sb_dblocks)
173                         return XFS_ERROR(EINVAL);
174         }
175         new = nb - mp->m_sb.sb_dblocks;
176         oagcount = mp->m_sb.sb_agcount;
177         if (nagcount > oagcount) {
178                 down_write(&mp->m_peraglock);
179                 mp->m_perag = kmem_realloc(mp->m_perag,
180                         sizeof(xfs_perag_t) * nagcount,
181                         sizeof(xfs_perag_t) * oagcount,
182                         KM_SLEEP);
183                 memset(&mp->m_perag[oagcount], 0,
184                         (nagcount - oagcount) * sizeof(xfs_perag_t));
185                 mp->m_flags |= XFS_MOUNT_32BITINODES;
186                 xfs_initialize_perag(mp, nagcount);
187                 up_write(&mp->m_peraglock);
188         }
189         tp = xfs_trans_alloc(mp, XFS_TRANS_GROWFS);
190         if ((error = xfs_trans_reserve(tp, XFS_GROWFS_SPACE_RES(mp),
191                         XFS_GROWDATA_LOG_RES(mp), 0, 0, 0))) {
192                 xfs_trans_cancel(tp, 0);
193                 return error;
194         }
195
196         nfree = 0;
197         for (agno = nagcount - 1; agno >= oagcount; agno--, new -= agsize) {
198                 /*
199                  * AG freelist header block
200                  */
201                 bp = xfs_buf_get(mp->m_ddev_targp,
202                                   XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)),
203                                   XFS_FSS_TO_BB(mp, 1), 0);
204                 agf = XFS_BUF_TO_AGF(bp);
205                 memset(agf, 0, mp->m_sb.sb_sectsize);
206                 INT_SET(agf->agf_magicnum, ARCH_CONVERT, XFS_AGF_MAGIC);
207                 INT_SET(agf->agf_versionnum, ARCH_CONVERT, XFS_AGF_VERSION);
208                 INT_SET(agf->agf_seqno, ARCH_CONVERT, agno);
209                 if (agno == nagcount - 1)
210                         agsize =
211                                 nb -
212                                 (agno * (xfs_rfsblock_t)mp->m_sb.sb_agblocks);
213                 else
214                         agsize = mp->m_sb.sb_agblocks;
215                 INT_SET(agf->agf_length, ARCH_CONVERT, agsize);
216                 INT_SET(agf->agf_roots[XFS_BTNUM_BNOi], ARCH_CONVERT,
217                         XFS_BNO_BLOCK(mp));
218                 INT_SET(agf->agf_roots[XFS_BTNUM_CNTi], ARCH_CONVERT,
219                         XFS_CNT_BLOCK(mp));
220                 INT_SET(agf->agf_levels[XFS_BTNUM_BNOi], ARCH_CONVERT, 1);
221                 INT_SET(agf->agf_levels[XFS_BTNUM_CNTi], ARCH_CONVERT, 1);
222                 INT_ZERO(agf->agf_flfirst, ARCH_CONVERT);
223                 INT_SET(agf->agf_fllast, ARCH_CONVERT, XFS_AGFL_SIZE(mp) - 1);
224                 INT_ZERO(agf->agf_flcount, ARCH_CONVERT);
225                 tmpsize = agsize - XFS_PREALLOC_BLOCKS(mp);
226                 INT_SET(agf->agf_freeblks, ARCH_CONVERT, tmpsize);
227                 INT_SET(agf->agf_longest, ARCH_CONVERT, tmpsize);
228                 error = xfs_bwrite(mp, bp);
229                 if (error) {
230                         goto error0;
231                 }
232                 /*
233                  * AG inode header block
234                  */
235                 bp = xfs_buf_get(mp->m_ddev_targp,
236                                   XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)),
237                                   XFS_FSS_TO_BB(mp, 1), 0);
238                 agi = XFS_BUF_TO_AGI(bp);
239                 memset(agi, 0, mp->m_sb.sb_sectsize);
240                 INT_SET(agi->agi_magicnum, ARCH_CONVERT, XFS_AGI_MAGIC);
241                 INT_SET(agi->agi_versionnum, ARCH_CONVERT, XFS_AGI_VERSION);
242                 INT_SET(agi->agi_seqno, ARCH_CONVERT, agno);
243                 INT_SET(agi->agi_length, ARCH_CONVERT, agsize);
244                 INT_ZERO(agi->agi_count, ARCH_CONVERT);
245                 INT_SET(agi->agi_root, ARCH_CONVERT, XFS_IBT_BLOCK(mp));
246                 INT_SET(agi->agi_level, ARCH_CONVERT, 1);
247                 INT_ZERO(agi->agi_freecount, ARCH_CONVERT);
248                 INT_SET(agi->agi_newino, ARCH_CONVERT, NULLAGINO);
249                 INT_SET(agi->agi_dirino, ARCH_CONVERT, NULLAGINO);
250                 for (bucket = 0; bucket < XFS_AGI_UNLINKED_BUCKETS; bucket++)
251                         INT_SET(agi->agi_unlinked[bucket], ARCH_CONVERT,
252                                 NULLAGINO);
253                 error = xfs_bwrite(mp, bp);
254                 if (error) {
255                         goto error0;
256                 }
257                 /*
258                  * BNO btree root block
259                  */
260                 bp = xfs_buf_get(mp->m_ddev_targp,
261                         XFS_AGB_TO_DADDR(mp, agno, XFS_BNO_BLOCK(mp)),
262                         BTOBB(mp->m_sb.sb_blocksize), 0);
263                 block = XFS_BUF_TO_SBLOCK(bp);
264                 memset(block, 0, mp->m_sb.sb_blocksize);
265                 INT_SET(block->bb_magic, ARCH_CONVERT, XFS_ABTB_MAGIC);
266                 INT_ZERO(block->bb_level, ARCH_CONVERT);
267                 INT_SET(block->bb_numrecs, ARCH_CONVERT, 1);
268                 INT_SET(block->bb_leftsib, ARCH_CONVERT, NULLAGBLOCK);
269                 INT_SET(block->bb_rightsib, ARCH_CONVERT, NULLAGBLOCK);
270                 arec = XFS_BTREE_REC_ADDR(mp->m_sb.sb_blocksize, xfs_alloc,
271                         block, 1, mp->m_alloc_mxr[0]);
272                 INT_SET(arec->ar_startblock, ARCH_CONVERT,
273                         XFS_PREALLOC_BLOCKS(mp));
274                 INT_SET(arec->ar_blockcount, ARCH_CONVERT,
275                         agsize - INT_GET(arec->ar_startblock, ARCH_CONVERT));
276                 error = xfs_bwrite(mp, bp);
277                 if (error) {
278                         goto error0;
279                 }
280                 /*
281                  * CNT btree root block
282                  */
283                 bp = xfs_buf_get(mp->m_ddev_targp,
284                         XFS_AGB_TO_DADDR(mp, agno, XFS_CNT_BLOCK(mp)),
285                         BTOBB(mp->m_sb.sb_blocksize), 0);
286                 block = XFS_BUF_TO_SBLOCK(bp);
287                 memset(block, 0, mp->m_sb.sb_blocksize);
288                 INT_SET(block->bb_magic, ARCH_CONVERT, XFS_ABTC_MAGIC);
289                 INT_ZERO(block->bb_level, ARCH_CONVERT);
290                 INT_SET(block->bb_numrecs, ARCH_CONVERT, 1);
291                 INT_SET(block->bb_leftsib, ARCH_CONVERT, NULLAGBLOCK);
292                 INT_SET(block->bb_rightsib, ARCH_CONVERT, NULLAGBLOCK);
293                 arec = XFS_BTREE_REC_ADDR(mp->m_sb.sb_blocksize, xfs_alloc,
294                         block, 1, mp->m_alloc_mxr[0]);
295                 INT_SET(arec->ar_startblock, ARCH_CONVERT,
296                         XFS_PREALLOC_BLOCKS(mp));
297                 INT_SET(arec->ar_blockcount, ARCH_CONVERT,
298                         agsize - INT_GET(arec->ar_startblock, ARCH_CONVERT));
299                 nfree += INT_GET(arec->ar_blockcount, ARCH_CONVERT);
300                 error = xfs_bwrite(mp, bp);
301                 if (error) {
302                         goto error0;
303                 }
304                 /*
305                  * INO btree root block
306                  */
307                 bp = xfs_buf_get(mp->m_ddev_targp,
308                         XFS_AGB_TO_DADDR(mp, agno, XFS_IBT_BLOCK(mp)),
309                         BTOBB(mp->m_sb.sb_blocksize), 0);
310                 block = XFS_BUF_TO_SBLOCK(bp);
311                 memset(block, 0, mp->m_sb.sb_blocksize);
312                 INT_SET(block->bb_magic, ARCH_CONVERT, XFS_IBT_MAGIC);
313                 INT_ZERO(block->bb_level, ARCH_CONVERT);
314                 INT_ZERO(block->bb_numrecs, ARCH_CONVERT);
315                 INT_SET(block->bb_leftsib, ARCH_CONVERT, NULLAGBLOCK);
316                 INT_SET(block->bb_rightsib, ARCH_CONVERT, NULLAGBLOCK);
317                 error = xfs_bwrite(mp, bp);
318                 if (error) {
319                         goto error0;
320                 }
321         }
322         xfs_trans_agblocks_delta(tp, nfree);
323         /*
324          * There are new blocks in the old last a.g.
325          */
326         if (new) {
327                 /*
328                  * Change the agi length.
329                  */
330                 error = xfs_ialloc_read_agi(mp, tp, agno, &bp);
331                 if (error) {
332                         goto error0;
333                 }
334                 ASSERT(bp);
335                 agi = XFS_BUF_TO_AGI(bp);
336                 INT_MOD(agi->agi_length, ARCH_CONVERT, new);
337                 ASSERT(nagcount == oagcount ||
338                        INT_GET(agi->agi_length, ARCH_CONVERT) ==
339                                 mp->m_sb.sb_agblocks);
340                 xfs_ialloc_log_agi(tp, bp, XFS_AGI_LENGTH);
341                 /*
342                  * Change agf length.
343                  */
344                 error = xfs_alloc_read_agf(mp, tp, agno, 0, &bp);
345                 if (error) {
346                         goto error0;
347                 }
348                 ASSERT(bp);
349                 agf = XFS_BUF_TO_AGF(bp);
350                 INT_MOD(agf->agf_length, ARCH_CONVERT, new);
351                 ASSERT(INT_GET(agf->agf_length, ARCH_CONVERT) ==
352                                 INT_GET(agi->agi_length, ARCH_CONVERT));
353                 /*
354                  * Free the new space.
355                  */
356                 error = xfs_free_extent(tp, XFS_AGB_TO_FSB(mp, agno,
357                         INT_GET(agf->agf_length, ARCH_CONVERT) - new), new);
358                 if (error) {
359                         goto error0;
360                 }
361         }
362         if (nagcount > oagcount)
363                 xfs_trans_mod_sb(tp, XFS_TRANS_SB_AGCOUNT, nagcount - oagcount);
364         if (nb > mp->m_sb.sb_dblocks)
365                 xfs_trans_mod_sb(tp, XFS_TRANS_SB_DBLOCKS,
366                                  nb - mp->m_sb.sb_dblocks);
367         if (nfree)
368                 xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, nfree);
369         if (dpct)
370                 xfs_trans_mod_sb(tp, XFS_TRANS_SB_IMAXPCT, dpct);
371         error = xfs_trans_commit(tp, 0, NULL);
372         if (error) {
373                 return error;
374         }
375         if (mp->m_sb.sb_imax_pct) {
376                 __uint64_t icount = mp->m_sb.sb_dblocks * mp->m_sb.sb_imax_pct;
377                 do_div(icount, 100);
378                 mp->m_maxicount = icount << mp->m_sb.sb_inopblog;
379         } else
380                 mp->m_maxicount = 0;
381         for (agno = 1; agno < nagcount; agno++) {
382                 error = xfs_read_buf(mp, mp->m_ddev_targp,
383                                   XFS_AGB_TO_DADDR(mp, agno, XFS_SB_BLOCK(mp)),
384                                   XFS_FSS_TO_BB(mp, 1), 0, &bp);
385                 if (error) {
386                         xfs_fs_cmn_err(CE_WARN, mp,
387                         "error %d reading secondary superblock for ag %d",
388                                 error, agno);
389                         break;
390                 }
391                 sbp = XFS_BUF_TO_SBP(bp);
392                 xfs_xlatesb(sbp, &mp->m_sb, -1, ARCH_CONVERT, XFS_SB_ALL_BITS);
393                 /*
394                  * If we get an error writing out the alternate superblocks,
395                  * just issue a warning and continue.  The real work is
396                  * already done and committed.
397                  */
398                 if (!(error = xfs_bwrite(mp, bp))) {
399                         continue;
400                 } else {
401                         xfs_fs_cmn_err(CE_WARN, mp,
402                 "write error %d updating secondary superblock for ag %d",
403                                 error, agno);
404                         break; /* no point in continuing */
405                 }
406         }
407         return 0;
408
409  error0:
410         xfs_trans_cancel(tp, XFS_TRANS_ABORT);
411         return error;
412 }
413
414 static int
415 xfs_growfs_log_private(
416         xfs_mount_t             *mp,    /* mount point for filesystem */
417         xfs_growfs_log_t        *in)    /* growfs log input struct */
418 {
419         xfs_extlen_t            nb;
420
421         nb = in->newblocks;
422         if (nb < XFS_MIN_LOG_BLOCKS || nb < XFS_B_TO_FSB(mp, XFS_MIN_LOG_BYTES))
423                 return XFS_ERROR(EINVAL);
424         if (nb == mp->m_sb.sb_logblocks &&
425             in->isint == (mp->m_sb.sb_logstart != 0))
426                 return XFS_ERROR(EINVAL);
427         /*
428          * Moving the log is hard, need new interfaces to sync
429          * the log first, hold off all activity while moving it.
430          * Can have shorter or longer log in the same space,
431          * or transform internal to external log or vice versa.
432          */
433         return XFS_ERROR(ENOSYS);
434 }
435
436 /*
437  * protected versions of growfs function acquire and release locks on the mount
438  * point - exported through ioctls: XFS_IOC_FSGROWFSDATA, XFS_IOC_FSGROWFSLOG,
439  * XFS_IOC_FSGROWFSRT
440  */
441
442
443 int
444 xfs_growfs_data(
445         xfs_mount_t             *mp,
446         xfs_growfs_data_t       *in)
447 {
448         int error;
449         if (!cpsema(&mp->m_growlock))
450                 return XFS_ERROR(EWOULDBLOCK);
451         error = xfs_growfs_data_private(mp, in);
452         vsema(&mp->m_growlock);
453         return error;
454 }
455
456 int
457 xfs_growfs_log(
458         xfs_mount_t             *mp,
459         xfs_growfs_log_t        *in)
460 {
461         int error;
462         if (!cpsema(&mp->m_growlock))
463                 return XFS_ERROR(EWOULDBLOCK);
464         error = xfs_growfs_log_private(mp, in);
465         vsema(&mp->m_growlock);
466         return error;
467 }
468
469 /*
470  * exported through ioctl XFS_IOC_FSCOUNTS
471  */
472
473 int
474 xfs_fs_counts(
475         xfs_mount_t             *mp,
476         xfs_fsop_counts_t       *cnt)
477 {
478         unsigned long   s;
479
480         s = XFS_SB_LOCK(mp);
481         cnt->freedata = mp->m_sb.sb_fdblocks;
482         cnt->freertx = mp->m_sb.sb_frextents;
483         cnt->freeino = mp->m_sb.sb_ifree;
484         cnt->allocino = mp->m_sb.sb_icount;
485         XFS_SB_UNLOCK(mp, s);
486         return 0;
487 }
488
489 /*
490  * exported through ioctl XFS_IOC_SET_RESBLKS & XFS_IOC_GET_RESBLKS
491  *
492  * xfs_reserve_blocks is called to set m_resblks
493  * in the in-core mount table. The number of unused reserved blocks
494  * is kept in m_resbls_avail.
495  *
496  * Reserve the requested number of blocks if available. Otherwise return
497  * as many as possible to satisfy the request. The actual number
498  * reserved are returned in outval
499  *
500  * A null inval pointer indicates that only the current reserved blocks
501  * available  should  be returned no settings are changed.
502  */
503
504 int
505 xfs_reserve_blocks(
506         xfs_mount_t             *mp,
507         __uint64_t              *inval,
508         xfs_fsop_resblks_t      *outval)
509 {
510         __uint64_t              lcounter, delta;
511         __uint64_t              request;
512         unsigned long s;
513
514         /* If inval is null, report current values and return */
515
516         if (inval == (__uint64_t *)NULL) {
517                 outval->resblks = mp->m_resblks;
518                 outval->resblks_avail = mp->m_resblks_avail;
519                 return(0);
520         }
521
522         request = *inval;
523         s = XFS_SB_LOCK(mp);
524
525         /*
526          * If our previous reservation was larger than the current value,
527          * then move any unused blocks back to the free pool.
528          */
529
530         if (mp->m_resblks > request) {
531                 lcounter = mp->m_resblks_avail - request;
532                 if (lcounter  > 0) {            /* release unused blocks */
533                         mp->m_sb.sb_fdblocks += lcounter;
534                         mp->m_resblks_avail -= lcounter;
535                 }
536                 mp->m_resblks = request;
537         } else {
538                 delta = request - mp->m_resblks;
539                 lcounter = mp->m_sb.sb_fdblocks;
540                 lcounter -= delta;
541                 if (lcounter < 0) {
542                         /* We can't satisfy the request, just get what we can */
543                         mp->m_resblks += mp->m_sb.sb_fdblocks;
544                         mp->m_resblks_avail += mp->m_sb.sb_fdblocks;
545                         mp->m_sb.sb_fdblocks = 0;
546                 } else {
547                         mp->m_sb.sb_fdblocks = lcounter;
548                         mp->m_resblks = request;
549                         mp->m_resblks_avail += delta;
550                 }
551         }
552
553         outval->resblks = mp->m_resblks;
554         outval->resblks_avail = mp->m_resblks_avail;
555         XFS_SB_UNLOCK(mp, s);
556         return(0);
557 }
558
559 void
560 xfs_fs_log_dummy(xfs_mount_t *mp)
561 {
562         xfs_trans_t *tp;
563         xfs_inode_t *ip;
564
565
566         tp = _xfs_trans_alloc(mp, XFS_TRANS_DUMMY1);
567         atomic_inc(&mp->m_active_trans);
568         if (xfs_trans_reserve(tp, 0, XFS_ICHANGE_LOG_RES(mp), 0, 0, 0)) {
569                 xfs_trans_cancel(tp, 0);
570                 return;
571         }
572
573         ip = mp->m_rootip;
574         xfs_ilock(ip, XFS_ILOCK_EXCL);
575
576         xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
577         xfs_trans_ihold(tp, ip);
578         xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
579         xfs_trans_set_sync(tp);
580         xfs_trans_commit(tp, 0, NULL);
581
582         xfs_iunlock(ip, XFS_ILOCK_EXCL);
583 }
584
585 int
586 xfs_fs_goingdown(
587         xfs_mount_t     *mp,
588         __uint32_t      inflags)
589 {
590         if (!capable(CAP_SYS_ADMIN))
591                 return -EPERM;
592
593         switch (inflags) {
594         case XFS_FSOP_GOING_FLAGS_DEFAULT: {
595                 struct vfs *vfsp = XFS_MTOVFS(mp);
596                 struct super_block *sb = freeze_bdev(vfsp->vfs_super->s_bdev);
597
598                 if (sb) {
599                         xfs_force_shutdown(mp, XFS_FORCE_UMOUNT);
600                         thaw_bdev(sb->s_bdev, sb);
601                 }
602
603                 break;
604         }
605         case XFS_FSOP_GOING_FLAGS_LOGFLUSH:
606                 xfs_force_shutdown(mp, XFS_FORCE_UMOUNT);
607                 break;
608         case XFS_FSOP_GOING_FLAGS_NOLOGFLUSH:
609                 xfs_force_shutdown(mp, XFS_FORCE_UMOUNT|XFS_LOG_IO_ERROR);
610                 break;
611         default:
612                 return XFS_ERROR(EINVAL);
613         }
614
615         return 0;
616 }