Commit 239dab46 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'for-linus-v3.11-rc1-2' of git://oss.sgi.com/xfs/xfs

Pull more xfs updates from Ben Myers:
 "Here are a fix for xfs_fsr, a cleanup in bulkstat, a cleanup in
  xfs_open_by_handle, updated mount options documentation, a cleanup in
  xfs_bmapi_write, a fix for the size of dquot log reservations, a fix
  for sgid inheritance when acls are in use, a fix for cleaning up
  quotainfo structures, and some more of the work which allows group and
  project quotas to be used together.

  We had a few more in this last quota category that we might have liked
  to get in, but it looks there are still a few items that need to be
  addressed.

   - fix for xfs_fsr returning -EINVAL
   - cleanup in xfs_bulkstat
   - cleanup in xfs_open_by_handle
   - update mount options documentation
   - clean up local format handling in xfs_bmapi_write
   - fix dquot log reservations which were too small
   - fix sgid inheritance for subdirectories when default acls are in use
   - add project quota fields to various structures
   - fix teardown of quotainfo structures when quotas are turned off"

* tag 'for-linus-v3.11-rc1-2' of git://oss.sgi.com/xfs/xfs:
  xfs: Fix the logic check for all quotas being turned off
  xfs: Add pquota fields where gquota is used.
  xfs: fix sgid inheritance for subdirectories inheriting default acls [V3]
  xfs: dquot log reservations are too small
  xfs: remove local fork format handling from xfs_bmapi_write()
  xfs: update mount options documentation
  xfs: use get_unused_fd_flags(0) instead of get_unused_fd()
  xfs: clean up unused codes at xfs_bulkstat()
  xfs: use XFS_BMAP_BMDR_SPACE vs. XFS_BROOT_SIZE_ADJ
parents f1c41088 c31ad439
...@@ -18,6 +18,8 @@ Mount Options ...@@ -18,6 +18,8 @@ Mount Options
============= =============
When mounting an XFS filesystem, the following options are accepted. When mounting an XFS filesystem, the following options are accepted.
For boolean mount options, the names with the (*) suffix is the
default behaviour.
allocsize=size allocsize=size
Sets the buffered I/O end-of-file preallocation size when Sets the buffered I/O end-of-file preallocation size when
...@@ -25,97 +27,128 @@ When mounting an XFS filesystem, the following options are accepted. ...@@ -25,97 +27,128 @@ When mounting an XFS filesystem, the following options are accepted.
Valid values for this option are page size (typically 4KiB) Valid values for this option are page size (typically 4KiB)
through to 1GiB, inclusive, in power-of-2 increments. through to 1GiB, inclusive, in power-of-2 increments.
attr2/noattr2 The default behaviour is for dynamic end-of-file
The options enable/disable (default is disabled for backward preallocation size, which uses a set of heuristics to
compatibility on-disk) an "opportunistic" improvement to be optimise the preallocation size based on the current
made in the way inline extended attributes are stored on-disk. allocation patterns within the file and the access patterns
When the new form is used for the first time (by setting or to the file. Specifying a fixed allocsize value turns off
removing extended attributes) the on-disk superblock feature the dynamic behaviour.
bit field will be updated to reflect this format being in use.
attr2
noattr2
The options enable/disable an "opportunistic" improvement to
be made in the way inline extended attributes are stored
on-disk. When the new form is used for the first time when
attr2 is selected (either when setting or removing extended
attributes) the on-disk superblock feature bit field will be
updated to reflect this format being in use.
The default behaviour is determined by the on-disk feature
bit indicating that attr2 behaviour is active. If either
mount option it set, then that becomes the new default used
by the filesystem.
CRC enabled filesystems always use the attr2 format, and so CRC enabled filesystems always use the attr2 format, and so
will reject the noattr2 mount option if it is set. will reject the noattr2 mount option if it is set.
barrier barrier (*)
Enables the use of block layer write barriers for writes into nobarrier
the journal and unwritten extent conversion. This allows for Enables/disables the use of block layer write barriers for
drive level write caching to be enabled, for devices that writes into the journal and for data integrity operations.
support write barriers. This allows for drive level write caching to be enabled, for
devices that support write barriers.
discard discard
Issue command to let the block device reclaim space freed by the nodiscard (*)
filesystem. This is useful for SSD devices, thinly provisioned Enable/disable the issuing of commands to let the block
LUNs and virtual machine images, but may have a performance device reclaim space freed by the filesystem. This is
impact. useful for SSD devices, thinly provisioned LUNs and virtual
machine images, but may have a performance impact.
dmapi
Enable the DMAPI (Data Management API) event callouts. Note: It is currently recommended that you use the fstrim
Use with the "mtpt" option. application to discard unused blocks rather than the discard
mount option because the performance impact of this option
grpid/bsdgroups and nogrpid/sysvgroups is quite severe.
These options define what group ID a newly created file gets.
When grpid is set, it takes the group ID of the directory in grpid/bsdgroups
which it is created; otherwise (the default) it takes the fsgid nogrpid/sysvgroups (*)
of the current process, unless the directory has the setgid bit These options define what group ID a newly created file
set, in which case it takes the gid from the parent directory, gets. When grpid is set, it takes the group ID of the
and also gets the setgid bit set if it is a directory itself. directory in which it is created; otherwise it takes the
fsgid of the current process, unless the directory has the
ihashsize=value setgid bit set, in which case it takes the gid from the
In memory inode hashes have been removed, so this option has parent directory, and also gets the setgid bit set if it is
no function as of August 2007. Option is deprecated. a directory itself.
ikeep/noikeep filestreams
When ikeep is specified, XFS does not delete empty inode clusters Make the data allocator use the filestreams allocation mode
and keeps them around on disk. ikeep is the traditional XFS across the entire filesystem rather than just on directories
behaviour. When noikeep is specified, empty inode clusters configured to use it.
are returned to the free space pool. The default is noikeep for
non-DMAPI mounts, while ikeep is the default when DMAPI is in use. ikeep
noikeep (*)
inode64 When ikeep is specified, XFS does not delete empty inode
Indicates that XFS is allowed to create inodes at any location clusters and keeps them around on disk. When noikeep is
in the filesystem, including those which will result in inode specified, empty inode clusters are returned to the free
numbers occupying more than 32 bits of significance. This is space pool.
the default allocation option. Applications which do not handle
inode numbers bigger than 32 bits, should use inode32 option.
inode32 inode32
Indicates that XFS is limited to create inodes at locations which inode64 (*)
will not result in inode numbers with more than 32 bits of When inode32 is specified, it indicates that XFS limits
significance. This is provided for backwards compatibility, since inode creation to locations which will not result in inode
64 bits inode numbers might cause problems for some applications numbers with more than 32 bits of significance.
that cannot handle large inode numbers.
When inode64 is specified, it indicates that XFS is allowed
largeio/nolargeio to create inodes at any location in the filesystem,
including those which will result in inode numbers occupying
more than 32 bits of significance.
inode32 is provided for backwards compatibility with older
systems and applications, since 64 bits inode numbers might
cause problems for some applications that cannot handle
large inode numbers. If applications are in use which do
not handle inode numbers bigger than 32 bits, the inode32
option should be specified.
largeio
nolargeio (*)
If "nolargeio" is specified, the optimal I/O reported in If "nolargeio" is specified, the optimal I/O reported in
st_blksize by stat(2) will be as small as possible to allow user st_blksize by stat(2) will be as small as possible to allow
applications to avoid inefficient read/modify/write I/O. user applications to avoid inefficient read/modify/write
If "largeio" specified, a filesystem that has a "swidth" specified I/O. This is typically the page size of the machine, as
will return the "swidth" value (in bytes) in st_blksize. If the this is the granularity of the page cache.
filesystem does not have a "swidth" specified but does specify
an "allocsize" then "allocsize" (in bytes) will be returned If "largeio" specified, a filesystem that was created with a
instead. "swidth" specified will return the "swidth" value (in bytes)
If neither of these two options are specified, then filesystem in st_blksize. If the filesystem does not have a "swidth"
will behave as if "nolargeio" was specified. specified but does specify an "allocsize" then "allocsize"
(in bytes) will be returned instead. Otherwise the behaviour
is the same as if "nolargeio" was specified.
logbufs=value logbufs=value
Set the number of in-memory log buffers. Valid numbers range Set the number of in-memory log buffers. Valid numbers
from 2-8 inclusive. range from 2-8 inclusive.
The default value is 8 buffers for filesystems with a
blocksize of 64KiB, 4 buffers for filesystems with a blocksize The default value is 8 buffers.
of 32KiB, 3 buffers for filesystems with a blocksize of 16KiB
and 2 buffers for all other configurations. Increasing the If the memory cost of 8 log buffers is too high on small
number of buffers may increase performance on some workloads systems, then it may be reduced at some cost to performance
at the cost of the memory used for the additional log buffers on metadata intensive workloads. The logbsize option below
and their associated control structures. controls the size of each buffer and so is also relevent to
this case.
logbsize=value logbsize=value
Set the size of each in-memory log buffer. Set the size of each in-memory log buffer. The size may be
Size may be specified in bytes, or in kilobytes with a "k" suffix. specified in bytes, or in kilobytes with a "k" suffix.
Valid sizes for version 1 and version 2 logs are 16384 (16k) and Valid sizes for version 1 and version 2 logs are 16384 (16k)
32768 (32k). Valid sizes for version 2 logs also include and 32768 (32k). Valid sizes for version 2 logs also
65536 (64k), 131072 (128k) and 262144 (256k). include 65536 (64k), 131072 (128k) and 262144 (256k). The
The default value for machines with more than 32MiB of memory logbsize must be an integer multiple of the log
is 32768, machines with less memory use 16384 by default. stripe unit configured at mkfs time.
The default value for for version 1 logs is 32768, while the
default value for version 2 logs is MAX(32768, log_sunit).
logdev=device and rtdev=device logdev=device and rtdev=device
Use an external log (metadata journal) and/or real-time device. Use an external log (metadata journal) and/or real-time device.
...@@ -124,16 +157,11 @@ When mounting an XFS filesystem, the following options are accepted. ...@@ -124,16 +157,11 @@ When mounting an XFS filesystem, the following options are accepted.
optional, and the log section can be separate from the data optional, and the log section can be separate from the data
section or contained within it. section or contained within it.
mtpt=mountpoint
Use with the "dmapi" option. The value specified here will be
included in the DMAPI mount event, and should be the path of
the actual mountpoint that is used.
noalign noalign
Data allocations will not be aligned at stripe unit boundaries. Data allocations will not be aligned at stripe unit
boundaries. This is only relevant to filesystems created
noatime with non-zero data alignment parameters (sunit, swidth) by
Access timestamps are not updated when a file is read. mkfs.
norecovery norecovery
The filesystem will be mounted without running log recovery. The filesystem will be mounted without running log recovery.
...@@ -144,8 +172,14 @@ When mounting an XFS filesystem, the following options are accepted. ...@@ -144,8 +172,14 @@ When mounting an XFS filesystem, the following options are accepted.
the mount will fail. the mount will fail.
nouuid nouuid
Don't check for double mounted file systems using the file system uuid. Don't check for double mounted file systems using the file
This is useful to mount LVM snapshot volumes. system uuid. This is useful to mount LVM snapshot volumes,
and often used in combination with "norecovery" for mounting
read-only snapshots.
noquota
Forcibly turns off all quota accounting and enforcement
within the filesystem.
uquota/usrquota/uqnoenforce/quota uquota/usrquota/uqnoenforce/quota
User disk quota accounting enabled, and limits (optionally) User disk quota accounting enabled, and limits (optionally)
...@@ -160,24 +194,64 @@ When mounting an XFS filesystem, the following options are accepted. ...@@ -160,24 +194,64 @@ When mounting an XFS filesystem, the following options are accepted.
enforced. Refer to xfs_quota(8) for further details. enforced. Refer to xfs_quota(8) for further details.
sunit=value and swidth=value sunit=value and swidth=value
Used to specify the stripe unit and width for a RAID device or Used to specify the stripe unit and width for a RAID device
a stripe volume. "value" must be specified in 512-byte block or a stripe volume. "value" must be specified in 512-byte
units. block units. These options are only relevant to filesystems
If this option is not specified and the filesystem was made on that were created with non-zero data alignment parameters.
a stripe volume or the stripe width or unit were specified for
the RAID device at mkfs time, then the mount system call will The sunit and swidth parameters specified must be compatible
restore the value from the superblock. For filesystems that with the existing filesystem alignment characteristics. In
are made directly on RAID devices, these options can be used general, that means the only valid changes to sunit are
to override the information in the superblock if the underlying increasing it by a power-of-2 multiple. Valid swidth values
disk layout changes after the filesystem has been created. are any integer multiple of a valid sunit value.
The "swidth" option is required if the "sunit" option has been
specified, and must be a multiple of the "sunit" value. Typically the only time these mount options are necessary if
after an underlying RAID device has had it's geometry
modified, such as adding a new disk to a RAID5 lun and
reshaping it.
swalloc swalloc
Data allocations will be rounded up to stripe width boundaries Data allocations will be rounded up to stripe width boundaries
when the current end of file is being extended and the file when the current end of file is being extended and the file
size is larger than the stripe width size. size is larger than the stripe width size.
wsync
When specified, all filesystem namespace operations are
executed synchronously. This ensures that when the namespace
operation (create, unlink, etc) completes, the change to the
namespace is on stable storage. This is useful in HA setups
where failover must not result in clients seeing
inconsistent namespace presentation during or after a
failover event.
Deprecated Mount Options
========================
delaylog/nodelaylog
Delayed logging is the only logging method that XFS supports
now, so these mount options are now ignored.
Due for removal in 3.12.
ihashsize=value
In memory inode hashes have been removed, so this option has
no function as of August 2007. Option is deprecated.
Due for removal in 3.12.
irixsgid
This behaviour is now controlled by a sysctl, so the mount
option is ignored.
Due for removal in 3.12.
osyncisdsync
osyncisosync
O_SYNC and O_DSYNC are fully supported, so there is no need
for these options any more.
Due for removal in 3.12.
sysctls sysctls
======= =======
...@@ -189,15 +263,20 @@ The following sysctls are available for the XFS filesystem: ...@@ -189,15 +263,20 @@ The following sysctls are available for the XFS filesystem:
in /proc/fs/xfs/stat. It then immediately resets to "0". in /proc/fs/xfs/stat. It then immediately resets to "0".
fs.xfs.xfssyncd_centisecs (Min: 100 Default: 3000 Max: 720000) fs.xfs.xfssyncd_centisecs (Min: 100 Default: 3000 Max: 720000)
The interval at which the xfssyncd thread flushes metadata The interval at which the filesystem flushes metadata
out to disk. This thread will flush log activity out, and out to disk and runs internal cache cleanup routines.
do some processing on unlinked inodes.
fs.xfs.xfsbufd_centisecs (Min: 50 Default: 100 Max: 3000) fs.xfs.filestream_centisecs (Min: 1 Default: 3000 Max: 360000)
The interval at which xfsbufd scans the dirty metadata buffers list. The interval at which the filesystem ages filestreams cache
references and returns timed-out AGs back to the free stream
pool.
fs.xfs.age_buffer_centisecs (Min: 100 Default: 1500 Max: 720000) fs.xfs.speculative_prealloc_lifetime
The age at which xfsbufd flushes dirty metadata buffers to disk. (Units: seconds Min: 1 Default: 300 Max: 86400)
The interval at which the background scanning for inodes
with unused speculative preallocation runs. The scan
removes unused preallocation from clean inodes and releases
the unused space back to the free pool.
fs.xfs.error_level (Min: 0 Default: 3 Max: 11) fs.xfs.error_level (Min: 0 Default: 3 Max: 11)
A volume knob for error reporting when internal errors occur. A volume knob for error reporting when internal errors occur.
...@@ -254,9 +333,31 @@ The following sysctls are available for the XFS filesystem: ...@@ -254,9 +333,31 @@ The following sysctls are available for the XFS filesystem:
by the xfs_io(8) chattr command on a directory to be by the xfs_io(8) chattr command on a directory to be
inherited by files in that directory. inherited by files in that directory.
fs.xfs.inherit_nodefrag (Min: 0 Default: 1 Max: 1)
Setting this to "1" will cause the "nodefrag" flag set
by the xfs_io(8) chattr command on a directory to be
inherited by files in that directory.
fs.xfs.rotorstep (Min: 1 Default: 1 Max: 256) fs.xfs.rotorstep (Min: 1 Default: 1 Max: 256)
In "inode32" allocation mode, this option determines how many In "inode32" allocation mode, this option determines how many
files the allocator attempts to allocate in the same allocation files the allocator attempts to allocate in the same allocation
group before moving to the next allocation group. The intent group before moving to the next allocation group. The intent
is to control the rate at which the allocator moves between is to control the rate at which the allocator moves between
allocation groups when allocating extents for new files. allocation groups when allocating extents for new files.
Deprecated Sysctls
==================
fs.xfs.xfsbufd_centisecs (Min: 50 Default: 100 Max: 3000)
Dirty metadata is now tracked by the log subsystem and
flushing is driven by log space and idling demands. The
xfsbufd no longer exists, so this syctl does nothing.
Due for removal in 3.14.
fs.xfs.age_buffer_centisecs (Min: 100 Default: 1500 Max: 720000)
Dirty metadata is now tracked by the log subsystem and
flushing is driven by log space and idling demands. The
xfsbufd no longer exists, so this syctl does nothing.
Due for removal in 3.14.
...@@ -690,6 +690,8 @@ xfs_attr_shortform_to_leaf(xfs_da_args_t *args) ...@@ -690,6 +690,8 @@ xfs_attr_shortform_to_leaf(xfs_da_args_t *args)
sf = (xfs_attr_shortform_t *)tmpbuffer; sf = (xfs_attr_shortform_t *)tmpbuffer;
xfs_idata_realloc(dp, -size, XFS_ATTR_FORK); xfs_idata_realloc(dp, -size, XFS_ATTR_FORK);
xfs_bmap_local_to_extents_empty(dp, XFS_ATTR_FORK);
bp = NULL; bp = NULL;
error = xfs_da_grow_inode(args, &blkno); error = xfs_da_grow_inode(args, &blkno);
if (error) { if (error) {
......
...@@ -1161,6 +1161,24 @@ xfs_bmap_extents_to_btree( ...@@ -1161,6 +1161,24 @@ xfs_bmap_extents_to_btree(
* since the file data needs to get logged so things will stay consistent. * since the file data needs to get logged so things will stay consistent.
* (The bmap-level manipulations are ok, though). * (The bmap-level manipulations are ok, though).
*/ */
void
xfs_bmap_local_to_extents_empty(
struct xfs_inode *ip,
int whichfork)
{
struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork);
ASSERT(XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_LOCAL);
ASSERT(ifp->if_bytes == 0);
ASSERT(XFS_IFORK_NEXTENTS(ip, whichfork) == 0);
xfs_bmap_forkoff_reset(ip->i_mount, ip, whichfork);
ifp->if_flags &= ~XFS_IFINLINE;
ifp->if_flags |= XFS_IFEXTENTS;
XFS_IFORK_FMT_SET(ip, whichfork, XFS_DINODE_FMT_EXTENTS);
}
STATIC int /* error */ STATIC int /* error */
xfs_bmap_local_to_extents( xfs_bmap_local_to_extents(
xfs_trans_t *tp, /* transaction pointer */ xfs_trans_t *tp, /* transaction pointer */
...@@ -1174,9 +1192,12 @@ xfs_bmap_local_to_extents( ...@@ -1174,9 +1192,12 @@ xfs_bmap_local_to_extents(
struct xfs_inode *ip, struct xfs_inode *ip,
struct xfs_ifork *ifp)) struct xfs_ifork *ifp))
{ {
int error; /* error return value */ int error = 0;
int flags; /* logging flags returned */ int flags; /* logging flags returned */
xfs_ifork_t *ifp; /* inode fork pointer */ xfs_ifork_t *ifp; /* inode fork pointer */
xfs_alloc_arg_t args; /* allocation arguments */
xfs_buf_t *bp; /* buffer for extent block */
xfs_bmbt_rec_host_t *ep; /* extent record pointer */
/* /*
* We don't want to deal with the case of keeping inode data inline yet. * We don't want to deal with the case of keeping inode data inline yet.
...@@ -1185,15 +1206,17 @@ xfs_bmap_local_to_extents( ...@@ -1185,15 +1206,17 @@ xfs_bmap_local_to_extents(
ASSERT(!(S_ISREG(ip->i_d.di_mode) && whichfork == XFS_DATA_FORK)); ASSERT(!(S_ISREG(ip->i_d.di_mode) && whichfork == XFS_DATA_FORK));
ifp = XFS_IFORK_PTR(ip, whichfork); ifp = XFS_IFORK_PTR(ip, whichfork);
ASSERT(XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_LOCAL); ASSERT(XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_LOCAL);
if (!ifp->if_bytes) {
xfs_bmap_local_to_extents_empty(ip, whichfork);
flags = XFS_ILOG_CORE;
goto done;
}
flags = 0; flags = 0;
error = 0; error = 0;
if (ifp->if_bytes) { ASSERT((ifp->if_flags & (XFS_IFINLINE|XFS_IFEXTENTS|XFS_IFEXTIREC)) ==
xfs_alloc_arg_t args; /* allocation arguments */ XFS_IFINLINE);
xfs_buf_t *bp; /* buffer for extent block */
xfs_bmbt_rec_host_t *ep;/* extent record pointer */
ASSERT((ifp->if_flags &
(XFS_IFINLINE|XFS_IFEXTENTS|XFS_IFEXTIREC)) == XFS_IFINLINE);
memset(&args, 0, sizeof(args)); memset(&args, 0, sizeof(args));
args.tp = tp; args.tp = tp;
args.mp = ip->i_mount; args.mp = ip->i_mount;
...@@ -1226,8 +1249,10 @@ xfs_bmap_local_to_extents( ...@@ -1226,8 +1249,10 @@ xfs_bmap_local_to_extents(
/* account for the change in fork size and log everything */ /* account for the change in fork size and log everything */
xfs_trans_log_buf(tp, bp, 0, ifp->if_bytes - 1); xfs_trans_log_buf(tp, bp, 0, ifp->if_bytes - 1);
xfs_bmap_forkoff_reset(args.mp, ip, whichfork);
xfs_idata_realloc(ip, -ifp->if_bytes, whichfork); xfs_idata_realloc(ip, -ifp->if_bytes, whichfork);
xfs_bmap_local_to_extents_empty(ip, whichfork);
flags |= XFS_ILOG_CORE;
xfs_iext_add(ifp, 0, 1); xfs_iext_add(ifp, 0, 1);
ep = xfs_iext_get_ext(ifp, 0); ep = xfs_iext_get_ext(ifp, 0);
xfs_bmbt_set_allf(ep, 0, args.fsbno, 1, XFS_EXT_NORM); xfs_bmbt_set_allf(ep, 0, args.fsbno, 1, XFS_EXT_NORM);
...@@ -1239,14 +1264,7 @@ xfs_bmap_local_to_extents( ...@@ -1239,14 +1264,7 @@ xfs_bmap_local_to_extents(
xfs_trans_mod_dquot_byino(tp, ip, xfs_trans_mod_dquot_byino(tp, ip,
XFS_TRANS_DQ_BCOUNT, 1L); XFS_TRANS_DQ_BCOUNT, 1L);
flags |= xfs_ilog_fext(whichfork); flags |= xfs_ilog_fext(whichfork);
} else {
ASSERT(XFS_IFORK_NEXTENTS(ip, whichfork) == 0);
xfs_bmap_forkoff_reset(ip->i_mount, ip, whichfork);
}
ifp->if_flags &= ~XFS_IFINLINE;
ifp->if_flags |= XFS_IFEXTENTS;
XFS_IFORK_FMT_SET(ip, whichfork, XFS_DINODE_FMT_EXTENTS);
flags |= XFS_ILOG_CORE;
done: done:
*logflagsp = flags; *logflagsp = flags;
return error; return error;
...@@ -1322,25 +1340,6 @@ xfs_bmap_add_attrfork_extents( ...@@ -1322,25 +1340,6 @@ xfs_bmap_add_attrfork_extents(
return error; return error;
} }
/*
* Block initialisation function for local to extent format conversion.
*
* This shouldn't actually be called by anyone, so make sure debug kernels cause
* a noticable failure.
*/
STATIC void
xfs_bmap_local_to_extents_init_fn(
struct xfs_trans *tp,
struct xfs_buf *bp,
struct xfs_inode *ip,
struct xfs_ifork *ifp)
{
ASSERT(0);
bp->b_ops = &xfs_bmbt_buf_ops;
memcpy(bp->b_addr, ifp->if_u1.if_data, ifp->if_bytes);
xfs_trans_buf_set_type(tp, bp, XFS_BLFT_BTREE_BUF);
}
/* /*
* Called from xfs_bmap_add_attrfork to handle local format files. Each * Called from xfs_bmap_add_attrfork to handle local format files. Each
* different data fork content type needs a different callout to do the * different data fork content type needs a different callout to do the
...@@ -1381,9 +1380,9 @@ xfs_bmap_add_attrfork_local( ...@@ -1381,9 +1380,9 @@ xfs_bmap_add_attrfork_local(
flags, XFS_DATA_FORK, flags, XFS_DATA_FORK,
xfs_symlink_local_to_remote); xfs_symlink_local_to_remote);
return xfs_bmap_local_to_extents(tp, ip, firstblock, 1, flags, /* should only be called for types that support local format data */
XFS_DATA_FORK, ASSERT(0);
xfs_bmap_local_to_extents_init_fn); return EFSCORRUPTED;
} }
/* /*
...@@ -4907,20 +4906,19 @@ xfs_bmapi_write( ...@@ -4907,20 +4906,19 @@ xfs_bmapi_write(
orig_mval = mval; orig_mval = mval;
orig_nmap = *nmap; orig_nmap = *nmap;
#endif #endif
whichfork = (flags & XFS_BMAPI_ATTRFORK) ?
XFS_ATTR_FORK : XFS_DATA_FORK;
ASSERT(*nmap >= 1); ASSERT(*nmap >= 1);
ASSERT(*nmap <= XFS_BMAP_MAX_NMAP); ASSERT(*nmap <= XFS_BMAP_MAX_NMAP);
ASSERT(!(flags & XFS_BMAPI_IGSTATE)); ASSERT(!(flags & XFS_BMAPI_IGSTATE));
ASSERT(tp != NULL); ASSERT(tp != NULL);
ASSERT(len > 0); ASSERT(len > 0);
ASSERT(XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_LOCAL);
whichfork = (flags & XFS_BMAPI_ATTRFORK) ?
XFS_ATTR_FORK : XFS_DATA_FORK;
if (unlikely(XFS_TEST_ERROR( if (unlikely(XFS_TEST_ERROR(
(XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS && (XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS &&
XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE && XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE),
XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_LOCAL),
mp, XFS_ERRTAG_BMAPIFORMAT, XFS_RANDOM_BMAPIFORMAT))) { mp, XFS_ERRTAG_BMAPIFORMAT, XFS_RANDOM_BMAPIFORMAT))) {
XFS_ERROR_REPORT("xfs_bmapi_write", XFS_ERRLEVEL_LOW, mp); XFS_ERROR_REPORT("xfs_bmapi_write", XFS_ERRLEVEL_LOW, mp);
return XFS_ERROR(EFSCORRUPTED); return XFS_ERROR(EFSCORRUPTED);
...@@ -4933,37 +4931,6 @@ xfs_bmapi_write( ...@@ -4933,37 +4931,6 @@ xfs_bmapi_write(
XFS_STATS_INC(xs_blk_mapw); XFS_STATS_INC(xs_blk_mapw);
if (XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_LOCAL) {
/*
* XXX (dgc): This assumes we are only called for inodes that
* contain content neutral data in local format. Anything that
* contains caller-specific data in local format that needs
* transformation to move to a block format needs to do the
* conversion to extent format itself.
*
* Directory data forks and attribute forks handle this
* themselves, but with the addition of metadata verifiers every
* data fork in local format now contains caller specific data
* and as such conversion through this function is likely to be
* broken.
*
* The only likely user of this branch is for remote symlinks,
* but we cannot overwrite the data fork contents of the symlink
* (EEXIST occurs higher up the stack) and so it will never go
* from local format to extent format here. Hence I don't think
* this branch is ever executed intentionally and we should
* consider removing it and asserting that xfs_bmapi_write()
* cannot be called directly on local format forks. i.e. callers
* are completely responsible for local to extent format
* conversion, not xfs_bmapi_write().
*/
error = xfs_bmap_local_to_extents(tp, ip, firstblock, total,
&bma.logflags, whichfork,
xfs_bmap_local_to_extents_init_fn);
if (error)
goto error0;
}
if (*firstblock == NULLFSBLOCK) { if (*firstblock == NULLFSBLOCK) {
if (XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_BTREE) if (XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_BTREE)
bma.minleft = be16_to_cpu(ifp->if_broot->bb_level) + 1; bma.minleft = be16_to_cpu(ifp->if_broot->bb_level) + 1;
......
...@@ -172,6 +172,7 @@ void xfs_bmap_trace_exlist(struct xfs_inode *ip, xfs_extnum_t cnt, ...@@ -172,6 +172,7 @@ void xfs_bmap_trace_exlist(struct xfs_inode *ip, xfs_extnum_t cnt,
#endif #endif
int xfs_bmap_add_attrfork(struct xfs_inode *ip, int size, int rsvd); int xfs_bmap_add_attrfork(struct xfs_inode *ip, int size, int rsvd);
void xfs_bmap_local_to_extents_empty(struct xfs_inode *ip, int whichfork);
void xfs_bmap_add_free(xfs_fsblock_t bno, xfs_filblks_t len, void xfs_bmap_add_free(xfs_fsblock_t bno, xfs_filblks_t len,
struct xfs_bmap_free *flist, struct xfs_mount *mp); struct xfs_bmap_free *flist, struct xfs_mount *mp);
void xfs_bmap_cancel(struct xfs_bmap_free *flist); void xfs_bmap_cancel(struct xfs_bmap_free *flist);
......
...@@ -132,9 +132,6 @@ typedef enum xfs_dinode_fmt { ...@@ -132,9 +132,6 @@ typedef enum xfs_dinode_fmt {
#define XFS_LITINO(mp, version) \ #define XFS_LITINO(mp, version) \
((int)(((mp)->m_sb.sb_inodesize) - xfs_dinode_size(version))) ((int)(((mp)->m_sb.sb_inodesize) - xfs_dinode_size(version)))
#define XFS_BROOT_SIZE_ADJ(ip) \
(XFS_BMBT_BLOCK_LEN((ip)->i_mount) - sizeof(xfs_bmdr_block_t))
/* /*
* Inode data & attribute fork sizes, per inode. * Inode data & attribute fork sizes, per inode.
*/ */
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include "xfs_dinode.h" #include "xfs_dinode.h"
#include "xfs_inode.h" #include "xfs_inode.h"
#include "xfs_inode_item.h" #include "xfs_inode_item.h"
#include "xfs_bmap.h"
#include "xfs_buf_item.h" #include "xfs_buf_item.h"
#include "xfs_dir2.h" #include "xfs_dir2.h"
#include "xfs_dir2_format.h" #include "xfs_dir2_format.h"
...@@ -1164,13 +1165,15 @@ xfs_dir2_sf_to_block( ...@@ -1164,13 +1165,15 @@ xfs_dir2_sf_to_block(
__be16 *tagp; /* end of data entry */ __be16 *tagp; /* end of data entry */
xfs_trans_t *tp; /* transaction pointer */ xfs_trans_t *tp; /* transaction pointer */
struct xfs_name name; struct xfs_name name;
struct xfs_ifork *ifp;
trace_xfs_dir2_sf_to_block(args); trace_xfs_dir2_sf_to_block(args);
dp = args->dp; dp = args->dp;
tp = args->trans; tp = args->trans;
mp = dp->i_mount; mp = dp->i_mount;
ASSERT(dp->i_df.if_flags & XFS_IFINLINE); ifp = XFS_IFORK_PTR(dp, XFS_DATA_FORK);
ASSERT(ifp->if_flags & XFS_IFINLINE);
/* /*
* Bomb out if the shortform directory is way too short. * Bomb out if the shortform directory is way too short.
*/ */
...@@ -1179,22 +1182,23 @@ xfs_dir2_sf_to_block( ...@@ -1179,22 +1182,23 @@ xfs_dir2_sf_to_block(
return XFS_ERROR(EIO); return XFS_ERROR(EIO);
} }
oldsfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; oldsfp = (xfs_dir2_sf_hdr_t *)ifp->if_u1.if_data;
ASSERT(dp->i_df.if_bytes == dp->i_d.di_size); ASSERT(ifp->if_bytes == dp->i_d.di_size);
ASSERT(dp->i_df.if_u1.if_data != NULL); ASSERT(ifp->if_u1.if_data != NULL);
ASSERT(dp->i_d.di_size >= xfs_dir2_sf_hdr_size(oldsfp->i8count)); ASSERT(dp->i_d.di_size >= xfs_dir2_sf_hdr_size(oldsfp->i8count));
ASSERT(dp->i_d.di_nextents == 0);
/* /*
* Copy the directory into a temporary buffer. * Copy the directory into a temporary buffer.
* Then pitch the incore inode data so we can make extents. * Then pitch the incore inode data so we can make extents.
*/ */
sfp = kmem_alloc(dp->i_df.if_bytes, KM_SLEEP); sfp = kmem_alloc(ifp->if_bytes, KM_SLEEP);
memcpy(sfp, oldsfp, dp->i_df.if_bytes); memcpy(sfp, oldsfp, ifp->if_bytes);
xfs_idata_realloc(dp, -dp->i_df.if_bytes, XFS_DATA_FORK); xfs_idata_realloc(dp, -ifp->if_bytes, XFS_DATA_FORK);
xfs_bmap_local_to_extents_empty(dp, XFS_DATA_FORK);
dp->i_d.di_size = 0; dp->i_d.di_size = 0;
xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);
/* /*
* Add block 0 to the inode. * Add block 0 to the inode.
......
...@@ -936,6 +936,7 @@ xfs_qm_dqput_final( ...@@ -936,6 +936,7 @@ xfs_qm_dqput_final(
{ {
struct xfs_quotainfo *qi = dqp->q_mount->m_quotainfo; struct xfs_quotainfo *qi = dqp->q_mount->m_quotainfo;
struct xfs_dquot *gdqp; struct xfs_dquot *gdqp;
struct xfs_dquot *pdqp;
trace_xfs_dqput_free(dqp); trace_xfs_dqput_free(dqp);
...@@ -949,21 +950,29 @@ xfs_qm_dqput_final( ...@@ -949,21 +950,29 @@ xfs_qm_dqput_final(
/* /*
* If we just added a udquot to the freelist, then we want to release * If we just added a udquot to the freelist, then we want to release
* the gdquot reference that it (probably) has. Otherwise it'll keep * the gdquot/pdquot reference that it (probably) has. Otherwise it'll
* the gdquot from getting reclaimed. * keep the gdquot/pdquot from getting reclaimed.
*/ */
gdqp = dqp->q_gdquot; gdqp = dqp->q_gdquot;
if (gdqp) { if (gdqp) {
xfs_dqlock(gdqp); xfs_dqlock(gdqp);
dqp->q_gdquot = NULL; dqp->q_gdquot = NULL;
} }
pdqp = dqp->q_pdquot;
if (pdqp) {
xfs_dqlock(pdqp);
dqp->q_pdquot = NULL;
}
xfs_dqunlock(dqp); xfs_dqunlock(dqp);
/* /*
* If we had a group quota hint, release it now. * If we had a group/project quota hint, release it now.
*/ */
if (gdqp) if (gdqp)
xfs_qm_dqput(gdqp); xfs_qm_dqput(gdqp);
if (pdqp)
xfs_qm_dqput(pdqp);
} }
/* /*
......
...@@ -53,6 +53,7 @@ typedef struct xfs_dquot { ...@@ -53,6 +53,7 @@ typedef struct xfs_dquot {
xfs_fileoff_t q_fileoffset; /* offset in quotas file */ xfs_fileoff_t q_fileoffset; /* offset in quotas file */
struct xfs_dquot*q_gdquot; /* group dquot, hint only */ struct xfs_dquot*q_gdquot; /* group dquot, hint only */
struct xfs_dquot*q_pdquot; /* project dquot, hint only */
xfs_disk_dquot_t q_core; /* actual usage & quotas */ xfs_disk_dquot_t q_core; /* actual usage & quotas */
xfs_dq_logitem_t q_logitem; /* dquot log item */ xfs_dq_logitem_t q_logitem; /* dquot log item */
xfs_qcnt_t q_res_bcount; /* total regular nblks used+reserved */ xfs_qcnt_t q_res_bcount; /* total regular nblks used+reserved */
...@@ -118,8 +119,9 @@ static inline int xfs_this_quota_on(struct xfs_mount *mp, int type) ...@@ -118,8 +119,9 @@ static inline int xfs_this_quota_on(struct xfs_mount *mp, int type)
case XFS_DQ_USER: case XFS_DQ_USER:
return XFS_IS_UQUOTA_ON(mp); return XFS_IS_UQUOTA_ON(mp);
case XFS_DQ_GROUP: case XFS_DQ_GROUP:
return XFS_IS_GQUOTA_ON(mp);
case XFS_DQ_PROJ: case XFS_DQ_PROJ:
return XFS_IS_OQUOTA_ON(mp); return XFS_IS_PQUOTA_ON(mp);
default: default:
return 0; return 0;
} }
...@@ -131,8 +133,9 @@ static inline xfs_dquot_t *xfs_inode_dquot(struct xfs_inode *ip, int type) ...@@ -131,8 +133,9 @@ static inline xfs_dquot_t *xfs_inode_dquot(struct xfs_inode *ip, int type)
case XFS_DQ_USER: case XFS_DQ_USER:
return ip->i_udquot; return ip->i_udquot;
case XFS_DQ_GROUP: case XFS_DQ_GROUP:
case XFS_DQ_PROJ:
return ip->i_gdquot; return ip->i_gdquot;
case XFS_DQ_PROJ:
return ip->i_pdquot;
default: default:
return NULL; return NULL;
} }
......
...@@ -337,6 +337,7 @@ xfs_iget_cache_miss( ...@@ -337,6 +337,7 @@ xfs_iget_cache_miss(
iflags |= XFS_IDONTCACHE; iflags |= XFS_IDONTCACHE;
ip->i_udquot = NULL; ip->i_udquot = NULL;
ip->i_gdquot = NULL; ip->i_gdquot = NULL;
ip->i_pdquot = NULL;
xfs_iflags_set(ip, iflags); xfs_iflags_set(ip, iflags);
/* insert the new inode */ /* insert the new inode */
......
...@@ -2156,8 +2156,8 @@ xfs_iroot_realloc( ...@@ -2156,8 +2156,8 @@ xfs_iroot_realloc(
np = (char *)XFS_BMAP_BROOT_PTR_ADDR(mp, ifp->if_broot, 1, np = (char *)XFS_BMAP_BROOT_PTR_ADDR(mp, ifp->if_broot, 1,
(int)new_size); (int)new_size);
ifp->if_broot_bytes = (int)new_size; ifp->if_broot_bytes = (int)new_size;
ASSERT(ifp->if_broot_bytes <= ASSERT(XFS_BMAP_BMDR_SPACE(ifp->if_broot) <=
XFS_IFORK_SIZE(ip, whichfork) + XFS_BROOT_SIZE_ADJ(ip)); XFS_IFORK_SIZE(ip, whichfork));
memmove(np, op, cur_max * (uint)sizeof(xfs_dfsbno_t)); memmove(np, op, cur_max * (uint)sizeof(xfs_dfsbno_t));
return; return;
} }
...@@ -2210,8 +2210,9 @@ xfs_iroot_realloc( ...@@ -2210,8 +2210,9 @@ xfs_iroot_realloc(
kmem_free(ifp->if_broot); kmem_free(ifp->if_broot);
ifp->if_broot = new_broot; ifp->if_broot = new_broot;
ifp->if_broot_bytes = (int)new_size; ifp->if_broot_bytes = (int)new_size;
ASSERT(ifp->if_broot_bytes <= if (ifp->if_broot)
XFS_IFORK_SIZE(ip, whichfork) + XFS_BROOT_SIZE_ADJ(ip)); ASSERT(XFS_BMAP_BMDR_SPACE(ifp->if_broot) <=
XFS_IFORK_SIZE(ip, whichfork));
return; return;
} }
...@@ -2522,9 +2523,8 @@ xfs_iflush_fork( ...@@ -2522,9 +2523,8 @@ xfs_iflush_fork(
if ((iip->ili_fields & brootflag[whichfork]) && if ((iip->ili_fields & brootflag[whichfork]) &&
(ifp->if_broot_bytes > 0)) { (ifp->if_broot_bytes > 0)) {
ASSERT(ifp->if_broot != NULL); ASSERT(ifp->if_broot != NULL);
ASSERT(ifp->if_broot_bytes <= ASSERT(XFS_BMAP_BMDR_SPACE(ifp->if_broot) <=
(XFS_IFORK_SIZE(ip, whichfork) + XFS_IFORK_SIZE(ip, whichfork));
XFS_BROOT_SIZE_ADJ(ip)));
xfs_bmbt_to_bmdr(mp, ifp->if_broot, ifp->if_broot_bytes, xfs_bmbt_to_bmdr(mp, ifp->if_broot, ifp->if_broot_bytes,
(xfs_bmdr_block_t *)cp, (xfs_bmdr_block_t *)cp,
XFS_DFORK_SIZE(dip, mp, whichfork)); XFS_DFORK_SIZE(dip, mp, whichfork));
......
...@@ -250,6 +250,7 @@ typedef struct xfs_inode { ...@@ -250,6 +250,7 @@ typedef struct xfs_inode {
struct xfs_mount *i_mount; /* fs mount struct ptr */ struct xfs_mount *i_mount; /* fs mount struct ptr */
struct xfs_dquot *i_udquot; /* user dquot */ struct xfs_dquot *i_udquot; /* user dquot */
struct xfs_dquot *i_gdquot; /* group dquot */ struct xfs_dquot *i_gdquot; /* group dquot */
struct xfs_dquot *i_pdquot; /* project dquot */
/* Inode location stuff */ /* Inode location stuff */
xfs_ino_t i_ino; /* inode number (agno/agino)*/ xfs_ino_t i_ino; /* inode number (agno/agino)*/
......
...@@ -248,7 +248,7 @@ xfs_open_by_handle( ...@@ -248,7 +248,7 @@ xfs_open_by_handle(
goto out_dput; goto out_dput;
} }
fd = get_unused_fd(); fd = get_unused_fd_flags(0);
if (fd < 0) { if (fd < 0) {
error = fd; error = fd;
goto out_dput; goto out_dput;
...@@ -928,7 +928,7 @@ xfs_ioctl_setattr( ...@@ -928,7 +928,7 @@ xfs_ioctl_setattr(
struct xfs_trans *tp; struct xfs_trans *tp;
unsigned int lock_flags = 0; unsigned int lock_flags = 0;
struct xfs_dquot *udqp = NULL; struct xfs_dquot *udqp = NULL;
struct xfs_dquot *gdqp = NULL; struct xfs_dquot *pdqp = NULL;
struct xfs_dquot *olddquot = NULL; struct xfs_dquot *olddquot = NULL;
int code; int code;
...@@ -957,7 +957,7 @@ xfs_ioctl_setattr( ...@@ -957,7 +957,7 @@ xfs_ioctl_setattr(
if (XFS_IS_QUOTA_ON(mp) && (mask & FSX_PROJID)) { if (XFS_IS_QUOTA_ON(mp) && (mask & FSX_PROJID)) {
code = xfs_qm_vop_dqalloc(ip, ip->i_d.di_uid, code = xfs_qm_vop_dqalloc(ip, ip->i_d.di_uid,
ip->i_d.di_gid, fa->fsx_projid, ip->i_d.di_gid, fa->fsx_projid,
XFS_QMOPT_PQUOTA, &udqp, &gdqp); XFS_QMOPT_PQUOTA, &udqp, NULL, &pdqp);
if (code) if (code)
return code; return code;
} }
...@@ -994,8 +994,8 @@ xfs_ioctl_setattr( ...@@ -994,8 +994,8 @@ xfs_ioctl_setattr(
XFS_IS_PQUOTA_ON(mp) && XFS_IS_PQUOTA_ON(mp) &&
xfs_get_projid(ip) != fa->fsx_projid) { xfs_get_projid(ip) != fa->fsx_projid) {
ASSERT(tp); ASSERT(tp);
code = xfs_qm_vop_chown_reserve(tp, ip, udqp, gdqp, code = xfs_qm_vop_chown_reserve(tp, ip, udqp, NULL,
capable(CAP_FOWNER) ? pdqp, capable(CAP_FOWNER) ?
XFS_QMOPT_FORCE_RES : 0); XFS_QMOPT_FORCE_RES : 0);
if (code) /* out of quota */ if (code) /* out of quota */
goto error_return; goto error_return;
...@@ -1113,7 +1113,7 @@ xfs_ioctl_setattr( ...@@ -1113,7 +1113,7 @@ xfs_ioctl_setattr(
if (xfs_get_projid(ip) != fa->fsx_projid) { if (xfs_get_projid(ip) != fa->fsx_projid) {
if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp)) { if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp)) {
olddquot = xfs_qm_vop_chown(tp, ip, olddquot = xfs_qm_vop_chown(tp, ip,
&ip->i_gdquot, gdqp); &ip->i_pdquot, pdqp);
} }
xfs_set_projid(ip, fa->fsx_projid); xfs_set_projid(ip, fa->fsx_projid);
...@@ -1160,13 +1160,13 @@ xfs_ioctl_setattr( ...@@ -1160,13 +1160,13 @@ xfs_ioctl_setattr(
*/ */
xfs_qm_dqrele(olddquot); xfs_qm_dqrele(olddquot);
xfs_qm_dqrele(udqp); xfs_qm_dqrele(udqp);
xfs_qm_dqrele(gdqp); xfs_qm_dqrele(pdqp);
return code; return code;
error_return: error_return:
xfs_qm_dqrele(udqp); xfs_qm_dqrele(udqp);
xfs_qm_dqrele(gdqp); xfs_qm_dqrele(pdqp);
xfs_trans_cancel(tp, 0); xfs_trans_cancel(tp, 0);
if (lock_flags) if (lock_flags)
xfs_iunlock(ip, lock_flags); xfs_iunlock(ip, lock_flags);
......
...@@ -467,9 +467,6 @@ xfs_setattr_mode( ...@@ -467,9 +467,6 @@ xfs_setattr_mode(
ASSERT(tp); ASSERT(tp);
ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))
mode &= ~S_ISGID;
ip->i_d.di_mode &= S_IFMT; ip->i_d.di_mode &= S_IFMT;
ip->i_d.di_mode |= mode & ~S_IFMT; ip->i_d.di_mode |= mode & ~S_IFMT;
...@@ -495,6 +492,8 @@ xfs_setattr_nonsize( ...@@ -495,6 +492,8 @@ xfs_setattr_nonsize(
trace_xfs_setattr(ip); trace_xfs_setattr(ip);
/* If acls are being inherited, we already have this checked */
if (!(flags & XFS_ATTR_NOACL)) {
if (mp->m_flags & XFS_MOUNT_RDONLY) if (mp->m_flags & XFS_MOUNT_RDONLY)
return XFS_ERROR(EROFS); return XFS_ERROR(EROFS);
...@@ -504,6 +503,7 @@ xfs_setattr_nonsize( ...@@ -504,6 +503,7 @@ xfs_setattr_nonsize(
error = -inode_change_ok(inode, iattr); error = -inode_change_ok(inode, iattr);
if (error) if (error)
return XFS_ERROR(error); return XFS_ERROR(error);
}
ASSERT((mask & ATTR_SIZE) == 0); ASSERT((mask & ATTR_SIZE) == 0);
...@@ -539,7 +539,7 @@ xfs_setattr_nonsize( ...@@ -539,7 +539,7 @@ xfs_setattr_nonsize(
ASSERT(udqp == NULL); ASSERT(udqp == NULL);
ASSERT(gdqp == NULL); ASSERT(gdqp == NULL);
error = xfs_qm_vop_dqalloc(ip, uid, gid, xfs_get_projid(ip), error = xfs_qm_vop_dqalloc(ip, uid, gid, xfs_get_projid(ip),
qflags, &udqp, &gdqp); qflags, &udqp, &gdqp, NULL);
if (error) if (error)
return error; return error;
} }
...@@ -575,7 +575,7 @@ xfs_setattr_nonsize( ...@@ -575,7 +575,7 @@ xfs_setattr_nonsize(
(XFS_IS_GQUOTA_ON(mp) && igid != gid))) { (XFS_IS_GQUOTA_ON(mp) && igid != gid))) {
ASSERT(tp); ASSERT(tp);
error = xfs_qm_vop_chown_reserve(tp, ip, udqp, gdqp, error = xfs_qm_vop_chown_reserve(tp, ip, udqp, gdqp,
capable(CAP_FOWNER) ? NULL, capable(CAP_FOWNER) ?
XFS_QMOPT_FORCE_RES : 0); XFS_QMOPT_FORCE_RES : 0);
if (error) /* out of quota */ if (error) /* out of quota */
goto out_trans_cancel; goto out_trans_cancel;
......
...@@ -221,7 +221,6 @@ xfs_bulkstat( ...@@ -221,7 +221,6 @@ xfs_bulkstat(
char __user *ubufp; /* pointer into user's buffer */ char __user *ubufp; /* pointer into user's buffer */
int ubelem; /* spaces used in user's buffer */ int ubelem; /* spaces used in user's buffer */
int ubused; /* bytes used by formatter */ int ubused; /* bytes used by formatter */
xfs_buf_t *bp; /* ptr to on-disk inode cluster buf */
/* /*
* Get the last inode value, see if there's nothing to do. * Get the last inode value, see if there's nothing to do.
...@@ -263,7 +262,6 @@ xfs_bulkstat( ...@@ -263,7 +262,6 @@ xfs_bulkstat(
rval = 0; rval = 0;
while (XFS_BULKSTAT_UBLEFT(ubleft) && agno < mp->m_sb.sb_agcount) { while (XFS_BULKSTAT_UBLEFT(ubleft) && agno < mp->m_sb.sb_agcount) {
cond_resched(); cond_resched();
bp = NULL;
error = xfs_ialloc_read_agi(mp, NULL, agno, &agbp); error = xfs_ialloc_read_agi(mp, NULL, agno, &agbp);
if (error) { if (error) {
/* /*
...@@ -436,27 +434,7 @@ xfs_bulkstat( ...@@ -436,27 +434,7 @@ xfs_bulkstat(
irbp->ir_freecount < XFS_INODES_PER_CHUNK; irbp->ir_freecount < XFS_INODES_PER_CHUNK;
chunkidx++, clustidx++, agino++) { chunkidx++, clustidx++, agino++) {
ASSERT(chunkidx < XFS_INODES_PER_CHUNK); ASSERT(chunkidx < XFS_INODES_PER_CHUNK);
/*
* Recompute agbno if this is the
* first inode of the cluster.
*
* Careful with clustidx. There can be
* multiple clusters per chunk, a single
* cluster per chunk or a cluster that has
* inodes represented from several different
* chunks (if blocksize is large).
*
* Because of this, the starting clustidx is
* initialized to zero in this loop but must
* later be reset after reading in the cluster
* buffer.
*/
if ((chunkidx & (nicluster - 1)) == 0) {
agbno = XFS_AGINO_TO_AGBNO(mp,
irbp->ir_startino) +
((chunkidx & nimask) >>
mp->m_sb.sb_inopblog);
}
ino = XFS_AGINO_TO_INO(mp, agno, agino); ino = XFS_AGINO_TO_INO(mp, agno, agino);
/* /*
* Skip if this inode is free. * Skip if this inode is free.
...@@ -502,10 +480,6 @@ xfs_bulkstat( ...@@ -502,10 +480,6 @@ xfs_bulkstat(
cond_resched(); cond_resched();
} }
if (bp)
xfs_buf_relse(bp);
/* /*
* Set up for the next loop iteration. * Set up for the next loop iteration.
*/ */
......
...@@ -137,6 +137,7 @@ xfs_qm_dqpurge( ...@@ -137,6 +137,7 @@ xfs_qm_dqpurge(
struct xfs_mount *mp = dqp->q_mount; struct xfs_mount *mp = dqp->q_mount;
struct xfs_quotainfo *qi = mp->m_quotainfo; struct xfs_quotainfo *qi = mp->m_quotainfo;
struct xfs_dquot *gdqp = NULL; struct xfs_dquot *gdqp = NULL;
struct xfs_dquot *pdqp = NULL;
xfs_dqlock(dqp); xfs_dqlock(dqp);
if ((dqp->dq_flags & XFS_DQ_FREEING) || dqp->q_nrefs != 0) { if ((dqp->dq_flags & XFS_DQ_FREEING) || dqp->q_nrefs != 0) {
...@@ -145,8 +146,7 @@ xfs_qm_dqpurge( ...@@ -145,8 +146,7 @@ xfs_qm_dqpurge(
} }
/* /*
* If this quota has a group hint attached, prepare for releasing it * If this quota has a hint attached, prepare for releasing it now.
* now.
*/ */
gdqp = dqp->q_gdquot; gdqp = dqp->q_gdquot;
if (gdqp) { if (gdqp) {
...@@ -154,6 +154,12 @@ xfs_qm_dqpurge( ...@@ -154,6 +154,12 @@ xfs_qm_dqpurge(
dqp->q_gdquot = NULL; dqp->q_gdquot = NULL;
} }
pdqp = dqp->q_pdquot;
if (pdqp) {
xfs_dqlock(pdqp);
dqp->q_pdquot = NULL;
}
dqp->dq_flags |= XFS_DQ_FREEING; dqp->dq_flags |= XFS_DQ_FREEING;
xfs_dqflock(dqp); xfs_dqflock(dqp);
...@@ -208,6 +214,8 @@ xfs_qm_dqpurge( ...@@ -208,6 +214,8 @@ xfs_qm_dqpurge(
if (gdqp) if (gdqp)
xfs_qm_dqput(gdqp); xfs_qm_dqput(gdqp);
if (pdqp)
xfs_qm_dqput(pdqp);
return 0; return 0;
} }
...@@ -364,6 +372,10 @@ xfs_qm_unmount_quotas( ...@@ -364,6 +372,10 @@ xfs_qm_unmount_quotas(
IRELE(mp->m_quotainfo->qi_gquotaip); IRELE(mp->m_quotainfo->qi_gquotaip);
mp->m_quotainfo->qi_gquotaip = NULL; mp->m_quotainfo->qi_gquotaip = NULL;
} }
if (mp->m_quotainfo->qi_pquotaip) {
IRELE(mp->m_quotainfo->qi_pquotaip);
mp->m_quotainfo->qi_pquotaip = NULL;
}
} }
} }
...@@ -410,7 +422,10 @@ xfs_qm_dqattach_one( ...@@ -410,7 +422,10 @@ xfs_qm_dqattach_one(
* be reclaimed as long as we have a ref from inode and we * be reclaimed as long as we have a ref from inode and we
* hold the ilock. * hold the ilock.
*/ */
if (type == XFS_DQ_GROUP)
dqp = udqhint->q_gdquot; dqp = udqhint->q_gdquot;
else
dqp = udqhint->q_pdquot;
if (dqp && be32_to_cpu(dqp->q_core.d_id) == id) { if (dqp && be32_to_cpu(dqp->q_core.d_id) == id) {
ASSERT(*IO_idqpp == NULL); ASSERT(*IO_idqpp == NULL);
...@@ -453,28 +468,42 @@ xfs_qm_dqattach_one( ...@@ -453,28 +468,42 @@ xfs_qm_dqattach_one(
/* /*
* Given a udquot and gdquot, attach a ptr to the group dquot in the * Given a udquot and group/project type, attach the group/project
* udquot as a hint for future lookups. * dquot pointer to the udquot as a hint for future lookups.
*/ */
STATIC void STATIC void
xfs_qm_dqattach_grouphint( xfs_qm_dqattach_hint(
xfs_dquot_t *udq, struct xfs_inode *ip,
xfs_dquot_t *gdq) int type)
{ {
xfs_dquot_t *tmp; struct xfs_dquot **dqhintp;
struct xfs_dquot *dqp;
struct xfs_dquot *udq = ip->i_udquot;
ASSERT(type == XFS_DQ_GROUP || type == XFS_DQ_PROJ);
xfs_dqlock(udq); xfs_dqlock(udq);
tmp = udq->q_gdquot; if (type == XFS_DQ_GROUP) {
if (tmp) { dqp = ip->i_gdquot;
if (tmp == gdq) dqhintp = &udq->q_gdquot;
} else {
dqp = ip->i_pdquot;
dqhintp = &udq->q_pdquot;
}
if (*dqhintp) {
struct xfs_dquot *tmp;
if (*dqhintp == dqp)
goto done; goto done;
udq->q_gdquot = NULL; tmp = *dqhintp;
*dqhintp = NULL;
xfs_qm_dqrele(tmp); xfs_qm_dqrele(tmp);
} }
udq->q_gdquot = xfs_qm_dqhold(gdq); *dqhintp = xfs_qm_dqhold(dqp);
done: done:
xfs_dqunlock(udq); xfs_dqunlock(udq);
} }
...@@ -527,12 +556,8 @@ xfs_qm_dqattach_locked( ...@@ -527,12 +556,8 @@ xfs_qm_dqattach_locked(
} }
ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
if (XFS_IS_OQUOTA_ON(mp)) { if (XFS_IS_GQUOTA_ON(mp)) {
error = XFS_IS_GQUOTA_ON(mp) ? error = xfs_qm_dqattach_one(ip, ip->i_d.di_gid, XFS_DQ_GROUP,
xfs_qm_dqattach_one(ip, ip->i_d.di_gid, XFS_DQ_GROUP,
flags & XFS_QMOPT_DQALLOC,
ip->i_udquot, &ip->i_gdquot) :
xfs_qm_dqattach_one(ip, xfs_get_projid(ip), XFS_DQ_PROJ,
flags & XFS_QMOPT_DQALLOC, flags & XFS_QMOPT_DQALLOC,
ip->i_udquot, &ip->i_gdquot); ip->i_udquot, &ip->i_gdquot);
/* /*
...@@ -544,14 +569,28 @@ xfs_qm_dqattach_locked( ...@@ -544,14 +569,28 @@ xfs_qm_dqattach_locked(
nquotas++; nquotas++;
} }
ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
if (XFS_IS_PQUOTA_ON(mp)) {
error = xfs_qm_dqattach_one(ip, xfs_get_projid(ip), XFS_DQ_PROJ,
flags & XFS_QMOPT_DQALLOC,
ip->i_udquot, &ip->i_pdquot);
/*
* Don't worry about the udquot that we may have
* attached above. It'll get detached, if not already.
*/
if (error)
goto done;
nquotas++;
}
/* /*
* Attach this group quota to the user quota as a hint. * Attach this group/project quota to the user quota as a hint.
* This WON'T, in general, result in a thrash. * This WON'T, in general, result in a thrash.
*/ */
if (nquotas == 2) { if (nquotas > 1 && ip->i_udquot) {
ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
ASSERT(ip->i_udquot); ASSERT(ip->i_gdquot || !XFS_IS_GQUOTA_ON(mp));
ASSERT(ip->i_gdquot); ASSERT(ip->i_pdquot || !XFS_IS_PQUOTA_ON(mp));
/* /*
* We do not have i_udquot locked at this point, but this check * We do not have i_udquot locked at this point, but this check
...@@ -560,7 +599,10 @@ xfs_qm_dqattach_locked( ...@@ -560,7 +599,10 @@ xfs_qm_dqattach_locked(
* succeed in general. * succeed in general.
*/ */
if (ip->i_udquot->q_gdquot != ip->i_gdquot) if (ip->i_udquot->q_gdquot != ip->i_gdquot)
xfs_qm_dqattach_grouphint(ip->i_udquot, ip->i_gdquot); xfs_qm_dqattach_hint(ip, XFS_DQ_GROUP);
if (ip->i_udquot->q_pdquot != ip->i_pdquot)
xfs_qm_dqattach_hint(ip, XFS_DQ_PROJ);
} }
done: done:
...@@ -568,8 +610,10 @@ xfs_qm_dqattach_locked( ...@@ -568,8 +610,10 @@ xfs_qm_dqattach_locked(
if (!error) { if (!error) {
if (XFS_IS_UQUOTA_ON(mp)) if (XFS_IS_UQUOTA_ON(mp))
ASSERT(ip->i_udquot); ASSERT(ip->i_udquot);
if (XFS_IS_OQUOTA_ON(mp)) if (XFS_IS_GQUOTA_ON(mp))
ASSERT(ip->i_gdquot); ASSERT(ip->i_gdquot);
if (XFS_IS_PQUOTA_ON(mp))
ASSERT(ip->i_pdquot);
} }
ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
#endif #endif
...@@ -602,7 +646,7 @@ void ...@@ -602,7 +646,7 @@ void
xfs_qm_dqdetach( xfs_qm_dqdetach(
xfs_inode_t *ip) xfs_inode_t *ip)
{ {
if (!(ip->i_udquot || ip->i_gdquot)) if (!(ip->i_udquot || ip->i_gdquot || ip->i_pdquot))
return; return;
trace_xfs_dquot_dqdetach(ip); trace_xfs_dquot_dqdetach(ip);
...@@ -616,6 +660,10 @@ xfs_qm_dqdetach( ...@@ -616,6 +660,10 @@ xfs_qm_dqdetach(
xfs_qm_dqrele(ip->i_gdquot); xfs_qm_dqrele(ip->i_gdquot);
ip->i_gdquot = NULL; ip->i_gdquot = NULL;
} }
if (ip->i_pdquot) {
xfs_qm_dqrele(ip->i_pdquot);
ip->i_pdquot = NULL;
}
} }
int int
...@@ -660,6 +708,7 @@ xfs_qm_init_quotainfo( ...@@ -660,6 +708,7 @@ xfs_qm_init_quotainfo(
INIT_RADIX_TREE(&qinf->qi_uquota_tree, GFP_NOFS); INIT_RADIX_TREE(&qinf->qi_uquota_tree, GFP_NOFS);
INIT_RADIX_TREE(&qinf->qi_gquota_tree, GFP_NOFS); INIT_RADIX_TREE(&qinf->qi_gquota_tree, GFP_NOFS);
INIT_RADIX_TREE(&qinf->qi_pquota_tree, GFP_NOFS);
mutex_init(&qinf->qi_tree_lock); mutex_init(&qinf->qi_tree_lock);
INIT_LIST_HEAD(&qinf->qi_lru_list); INIT_LIST_HEAD(&qinf->qi_lru_list);
...@@ -761,6 +810,10 @@ xfs_qm_destroy_quotainfo( ...@@ -761,6 +810,10 @@ xfs_qm_destroy_quotainfo(
IRELE(qi->qi_gquotaip); IRELE(qi->qi_gquotaip);
qi->qi_gquotaip = NULL; qi->qi_gquotaip = NULL;
} }
if (qi->qi_pquotaip) {
IRELE(qi->qi_pquotaip);
qi->qi_pquotaip = NULL;
}
mutex_destroy(&qi->qi_quotaofflock); mutex_destroy(&qi->qi_quotaofflock);
kmem_free(qi); kmem_free(qi);
mp->m_quotainfo = NULL; mp->m_quotainfo = NULL;
...@@ -1269,13 +1322,14 @@ xfs_qm_quotacheck( ...@@ -1269,13 +1322,14 @@ xfs_qm_quotacheck(
LIST_HEAD (buffer_list); LIST_HEAD (buffer_list);
struct xfs_inode *uip = mp->m_quotainfo->qi_uquotaip; struct xfs_inode *uip = mp->m_quotainfo->qi_uquotaip;
struct xfs_inode *gip = mp->m_quotainfo->qi_gquotaip; struct xfs_inode *gip = mp->m_quotainfo->qi_gquotaip;
struct xfs_inode *pip = mp->m_quotainfo->qi_pquotaip;
count = INT_MAX; count = INT_MAX;
structsz = 1; structsz = 1;
lastino = 0; lastino = 0;
flags = 0; flags = 0;
ASSERT(uip || gip); ASSERT(uip || gip || pip);
ASSERT(XFS_IS_QUOTA_RUNNING(mp)); ASSERT(XFS_IS_QUOTA_RUNNING(mp));
xfs_notice(mp, "Quotacheck needed: Please wait."); xfs_notice(mp, "Quotacheck needed: Please wait.");
...@@ -1294,13 +1348,19 @@ xfs_qm_quotacheck( ...@@ -1294,13 +1348,19 @@ xfs_qm_quotacheck(
} }
if (gip) { if (gip) {
error = xfs_qm_dqiterate(mp, gip, XFS_IS_GQUOTA_ON(mp) ? error = xfs_qm_dqiterate(mp, gip, XFS_QMOPT_GQUOTA,
XFS_QMOPT_GQUOTA : XFS_QMOPT_PQUOTA,
&buffer_list); &buffer_list);
if (error) if (error)
goto error_return; goto error_return;
flags |= XFS_IS_GQUOTA_ON(mp) ? flags |= XFS_GQUOTA_CHKD;
XFS_GQUOTA_CHKD : XFS_PQUOTA_CHKD; }
if (pip) {
error = xfs_qm_dqiterate(mp, pip, XFS_QMOPT_PQUOTA,
&buffer_list);
if (error)
goto error_return;
flags |= XFS_PQUOTA_CHKD;
} }
do { do {
...@@ -1397,6 +1457,7 @@ xfs_qm_init_quotainos( ...@@ -1397,6 +1457,7 @@ xfs_qm_init_quotainos(
{ {
struct xfs_inode *uip = NULL; struct xfs_inode *uip = NULL;
struct xfs_inode *gip = NULL; struct xfs_inode *gip = NULL;
struct xfs_inode *pip = NULL;
int error; int error;
__int64_t sbflags = 0; __int64_t sbflags = 0;
uint flags = 0; uint flags = 0;
...@@ -1415,7 +1476,7 @@ xfs_qm_init_quotainos( ...@@ -1415,7 +1476,7 @@ xfs_qm_init_quotainos(
if (error) if (error)
return XFS_ERROR(error); return XFS_ERROR(error);
} }
if (XFS_IS_OQUOTA_ON(mp) && if (XFS_IS_GQUOTA_ON(mp) &&
mp->m_sb.sb_gquotino != NULLFSINO) { mp->m_sb.sb_gquotino != NULLFSINO) {
ASSERT(mp->m_sb.sb_gquotino > 0); ASSERT(mp->m_sb.sb_gquotino > 0);
error = xfs_iget(mp, NULL, mp->m_sb.sb_gquotino, error = xfs_iget(mp, NULL, mp->m_sb.sb_gquotino,
...@@ -1423,6 +1484,15 @@ xfs_qm_init_quotainos( ...@@ -1423,6 +1484,15 @@ xfs_qm_init_quotainos(
if (error) if (error)
goto error_rele; goto error_rele;
} }
/* XXX: Use gquotino for now */
if (XFS_IS_PQUOTA_ON(mp) &&
mp->m_sb.sb_gquotino != NULLFSINO) {
ASSERT(mp->m_sb.sb_gquotino > 0);
error = xfs_iget(mp, NULL, mp->m_sb.sb_gquotino,
0, 0, &pip);
if (error)
goto error_rele;
}
} else { } else {
flags |= XFS_QMOPT_SBVERSION; flags |= XFS_QMOPT_SBVERSION;
sbflags |= (XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO | sbflags |= (XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO |
...@@ -1430,7 +1500,7 @@ xfs_qm_init_quotainos( ...@@ -1430,7 +1500,7 @@ xfs_qm_init_quotainos(
} }
/* /*
* Create the two inodes, if they don't exist already. The changes * Create the three inodes, if they don't exist already. The changes
* made above will get added to a transaction and logged in one of * made above will get added to a transaction and logged in one of
* the qino_alloc calls below. If the device is readonly, * the qino_alloc calls below. If the device is readonly,
* temporarily switch to read-write to do this. * temporarily switch to read-write to do this.
...@@ -1444,17 +1514,27 @@ xfs_qm_init_quotainos( ...@@ -1444,17 +1514,27 @@ xfs_qm_init_quotainos(
flags &= ~XFS_QMOPT_SBVERSION; flags &= ~XFS_QMOPT_SBVERSION;
} }
if (XFS_IS_OQUOTA_ON(mp) && gip == NULL) { if (XFS_IS_GQUOTA_ON(mp) && gip == NULL) {
flags |= (XFS_IS_GQUOTA_ON(mp) ?
XFS_QMOPT_GQUOTA : XFS_QMOPT_PQUOTA);
error = xfs_qm_qino_alloc(mp, &gip, error = xfs_qm_qino_alloc(mp, &gip,
sbflags | XFS_SB_GQUOTINO, flags); sbflags | XFS_SB_GQUOTINO,
flags | XFS_QMOPT_GQUOTA);
if (error)
goto error_rele;
flags &= ~XFS_QMOPT_SBVERSION;
}
if (XFS_IS_PQUOTA_ON(mp) && pip == NULL) {
/* XXX: Use XFS_SB_GQUOTINO for now */
error = xfs_qm_qino_alloc(mp, &pip,
sbflags | XFS_SB_GQUOTINO,
flags | XFS_QMOPT_PQUOTA);
if (error) if (error)
goto error_rele; goto error_rele;
} }
mp->m_quotainfo->qi_uquotaip = uip; mp->m_quotainfo->qi_uquotaip = uip;
mp->m_quotainfo->qi_gquotaip = gip; mp->m_quotainfo->qi_gquotaip = gip;
mp->m_quotainfo->qi_pquotaip = pip;
return 0; return 0;
...@@ -1463,6 +1543,8 @@ xfs_qm_init_quotainos( ...@@ -1463,6 +1543,8 @@ xfs_qm_init_quotainos(
IRELE(uip); IRELE(uip);
if (gip) if (gip)
IRELE(gip); IRELE(gip);
if (pip)
IRELE(pip);
return XFS_ERROR(error); return XFS_ERROR(error);
} }
...@@ -1657,11 +1739,13 @@ xfs_qm_vop_dqalloc( ...@@ -1657,11 +1739,13 @@ xfs_qm_vop_dqalloc(
prid_t prid, prid_t prid,
uint flags, uint flags,
struct xfs_dquot **O_udqpp, struct xfs_dquot **O_udqpp,
struct xfs_dquot **O_gdqpp) struct xfs_dquot **O_gdqpp,
struct xfs_dquot **O_pdqpp)
{ {
struct xfs_mount *mp = ip->i_mount; struct xfs_mount *mp = ip->i_mount;
struct xfs_dquot *uq = NULL; struct xfs_dquot *uq = NULL;
struct xfs_dquot *gq = NULL; struct xfs_dquot *gq = NULL;
struct xfs_dquot *pq = NULL;
int error; int error;
uint lockflags; uint lockflags;
...@@ -1741,24 +1825,25 @@ xfs_qm_vop_dqalloc( ...@@ -1741,24 +1825,25 @@ xfs_qm_vop_dqalloc(
ASSERT(ip->i_gdquot); ASSERT(ip->i_gdquot);
gq = xfs_qm_dqhold(ip->i_gdquot); gq = xfs_qm_dqhold(ip->i_gdquot);
} }
} else if ((flags & XFS_QMOPT_PQUOTA) && XFS_IS_PQUOTA_ON(mp)) { }
if ((flags & XFS_QMOPT_PQUOTA) && XFS_IS_PQUOTA_ON(mp)) {
if (xfs_get_projid(ip) != prid) { if (xfs_get_projid(ip) != prid) {
xfs_iunlock(ip, lockflags); xfs_iunlock(ip, lockflags);
error = xfs_qm_dqget(mp, NULL, (xfs_dqid_t)prid, error = xfs_qm_dqget(mp, NULL, (xfs_dqid_t)prid,
XFS_DQ_PROJ, XFS_DQ_PROJ,
XFS_QMOPT_DQALLOC | XFS_QMOPT_DQALLOC |
XFS_QMOPT_DOWARN, XFS_QMOPT_DOWARN,
&gq); &pq);
if (error) { if (error) {
ASSERT(error != ENOENT); ASSERT(error != ENOENT);
goto error_rele; goto error_rele;
} }
xfs_dqunlock(gq); xfs_dqunlock(pq);
lockflags = XFS_ILOCK_SHARED; lockflags = XFS_ILOCK_SHARED;
xfs_ilock(ip, lockflags); xfs_ilock(ip, lockflags);
} else { } else {
ASSERT(ip->i_gdquot); ASSERT(ip->i_pdquot);
gq = xfs_qm_dqhold(ip->i_gdquot); pq = xfs_qm_dqhold(ip->i_pdquot);
} }
} }
if (uq) if (uq)
...@@ -1773,9 +1858,15 @@ xfs_qm_vop_dqalloc( ...@@ -1773,9 +1858,15 @@ xfs_qm_vop_dqalloc(
*O_gdqpp = gq; *O_gdqpp = gq;
else if (gq) else if (gq)
xfs_qm_dqrele(gq); xfs_qm_dqrele(gq);
if (O_pdqpp)
*O_pdqpp = pq;
else if (pq)
xfs_qm_dqrele(pq);
return 0; return 0;
error_rele: error_rele:
if (gq)
xfs_qm_dqrele(gq);
if (uq) if (uq)
xfs_qm_dqrele(uq); xfs_qm_dqrele(uq);
return error; return error;
...@@ -1830,14 +1921,17 @@ xfs_qm_vop_chown_reserve( ...@@ -1830,14 +1921,17 @@ xfs_qm_vop_chown_reserve(
struct xfs_inode *ip, struct xfs_inode *ip,
struct xfs_dquot *udqp, struct xfs_dquot *udqp,
struct xfs_dquot *gdqp, struct xfs_dquot *gdqp,
struct xfs_dquot *pdqp,
uint flags) uint flags)
{ {
struct xfs_mount *mp = ip->i_mount; struct xfs_mount *mp = ip->i_mount;
uint delblks, blkflags, prjflags = 0; uint delblks, blkflags, prjflags = 0;
struct xfs_dquot *udq_unres = NULL; struct xfs_dquot *udq_unres = NULL;
struct xfs_dquot *gdq_unres = NULL; struct xfs_dquot *gdq_unres = NULL;
struct xfs_dquot *pdq_unres = NULL;
struct xfs_dquot *udq_delblks = NULL; struct xfs_dquot *udq_delblks = NULL;
struct xfs_dquot *gdq_delblks = NULL; struct xfs_dquot *gdq_delblks = NULL;
struct xfs_dquot *pdq_delblks = NULL;
int error; int error;
...@@ -1861,24 +1955,28 @@ xfs_qm_vop_chown_reserve( ...@@ -1861,24 +1955,28 @@ xfs_qm_vop_chown_reserve(
udq_unres = ip->i_udquot; udq_unres = ip->i_udquot;
} }
} }
if (XFS_IS_OQUOTA_ON(ip->i_mount) && gdqp) { if (XFS_IS_GQUOTA_ON(ip->i_mount) && gdqp &&
if (XFS_IS_PQUOTA_ON(ip->i_mount) && ip->i_d.di_gid != be32_to_cpu(gdqp->q_core.d_id)) {
xfs_get_projid(ip) != be32_to_cpu(gdqp->q_core.d_id))
prjflags = XFS_QMOPT_ENOSPC;
if (prjflags ||
(XFS_IS_GQUOTA_ON(ip->i_mount) &&
ip->i_d.di_gid != be32_to_cpu(gdqp->q_core.d_id))) {
gdq_delblks = gdqp; gdq_delblks = gdqp;
if (delblks) { if (delblks) {
ASSERT(ip->i_gdquot); ASSERT(ip->i_gdquot);
gdq_unres = ip->i_gdquot; gdq_unres = ip->i_gdquot;
} }
} }
if (XFS_IS_PQUOTA_ON(ip->i_mount) && pdqp &&
xfs_get_projid(ip) != be32_to_cpu(pdqp->q_core.d_id)) {
prjflags = XFS_QMOPT_ENOSPC;
pdq_delblks = pdqp;
if (delblks) {
ASSERT(ip->i_pdquot);
pdq_unres = ip->i_pdquot;
}
} }
error = xfs_trans_reserve_quota_bydquots(tp, ip->i_mount, error = xfs_trans_reserve_quota_bydquots(tp, ip->i_mount,
udq_delblks, gdq_delblks, ip->i_d.di_nblocks, 1, udq_delblks, gdq_delblks, pdq_delblks,
ip->i_d.di_nblocks, 1,
flags | blkflags | prjflags); flags | blkflags | prjflags);
if (error) if (error)
return error; return error;
...@@ -1893,16 +1991,17 @@ xfs_qm_vop_chown_reserve( ...@@ -1893,16 +1991,17 @@ xfs_qm_vop_chown_reserve(
/* /*
* Do the reservations first. Unreservation can't fail. * Do the reservations first. Unreservation can't fail.
*/ */
ASSERT(udq_delblks || gdq_delblks); ASSERT(udq_delblks || gdq_delblks || pdq_delblks);
ASSERT(udq_unres || gdq_unres); ASSERT(udq_unres || gdq_unres || pdq_unres);
error = xfs_trans_reserve_quota_bydquots(NULL, ip->i_mount, error = xfs_trans_reserve_quota_bydquots(NULL, ip->i_mount,
udq_delblks, gdq_delblks, (xfs_qcnt_t)delblks, 0, udq_delblks, gdq_delblks, pdq_delblks,
(xfs_qcnt_t)delblks, 0,
flags | blkflags | prjflags); flags | blkflags | prjflags);
if (error) if (error)
return error; return error;
xfs_trans_reserve_quota_bydquots(NULL, ip->i_mount, xfs_trans_reserve_quota_bydquots(NULL, ip->i_mount,
udq_unres, gdq_unres, -((xfs_qcnt_t)delblks), 0, udq_unres, gdq_unres, pdq_unres,
blkflags); -((xfs_qcnt_t)delblks), 0, blkflags);
} }
return (0); return (0);
...@@ -1941,7 +2040,8 @@ xfs_qm_vop_create_dqattach( ...@@ -1941,7 +2040,8 @@ xfs_qm_vop_create_dqattach(
struct xfs_trans *tp, struct xfs_trans *tp,
struct xfs_inode *ip, struct xfs_inode *ip,
struct xfs_dquot *udqp, struct xfs_dquot *udqp,
struct xfs_dquot *gdqp) struct xfs_dquot *gdqp,
struct xfs_dquot *pdqp)
{ {
struct xfs_mount *mp = tp->t_mountp; struct xfs_mount *mp = tp->t_mountp;
...@@ -1961,13 +2061,18 @@ xfs_qm_vop_create_dqattach( ...@@ -1961,13 +2061,18 @@ xfs_qm_vop_create_dqattach(
} }
if (gdqp) { if (gdqp) {
ASSERT(ip->i_gdquot == NULL); ASSERT(ip->i_gdquot == NULL);
ASSERT(XFS_IS_OQUOTA_ON(mp)); ASSERT(XFS_IS_GQUOTA_ON(mp));
ASSERT((XFS_IS_GQUOTA_ON(mp) ? ASSERT(ip->i_d.di_gid == be32_to_cpu(gdqp->q_core.d_id));
ip->i_d.di_gid : xfs_get_projid(ip)) ==
be32_to_cpu(gdqp->q_core.d_id));
ip->i_gdquot = xfs_qm_dqhold(gdqp); ip->i_gdquot = xfs_qm_dqhold(gdqp);
xfs_trans_mod_dquot(tp, gdqp, XFS_TRANS_DQ_ICOUNT, 1); xfs_trans_mod_dquot(tp, gdqp, XFS_TRANS_DQ_ICOUNT, 1);
} }
if (pdqp) {
ASSERT(ip->i_pdquot == NULL);
ASSERT(XFS_IS_PQUOTA_ON(mp));
ASSERT(xfs_get_projid(ip) == be32_to_cpu(pdqp->q_core.d_id));
ip->i_pdquot = xfs_qm_dqhold(pdqp);
xfs_trans_mod_dquot(tp, pdqp, XFS_TRANS_DQ_ICOUNT, 1);
}
} }
...@@ -44,9 +44,11 @@ extern struct kmem_zone *xfs_qm_dqtrxzone; ...@@ -44,9 +44,11 @@ extern struct kmem_zone *xfs_qm_dqtrxzone;
typedef struct xfs_quotainfo { typedef struct xfs_quotainfo {
struct radix_tree_root qi_uquota_tree; struct radix_tree_root qi_uquota_tree;
struct radix_tree_root qi_gquota_tree; struct radix_tree_root qi_gquota_tree;
struct radix_tree_root qi_pquota_tree;
struct mutex qi_tree_lock; struct mutex qi_tree_lock;
xfs_inode_t *qi_uquotaip; /* user quota inode */ struct xfs_inode *qi_uquotaip; /* user quota inode */
xfs_inode_t *qi_gquotaip; /* group quota inode */ struct xfs_inode *qi_gquotaip; /* group quota inode */
struct xfs_inode *qi_pquotaip; /* project quota inode */
struct list_head qi_lru_list; struct list_head qi_lru_list;
struct mutex qi_lru_lock; struct mutex qi_lru_lock;
int qi_lru_count; int qi_lru_count;
...@@ -78,8 +80,9 @@ xfs_dquot_tree( ...@@ -78,8 +80,9 @@ xfs_dquot_tree(
case XFS_DQ_USER: case XFS_DQ_USER:
return &qi->qi_uquota_tree; return &qi->qi_uquota_tree;
case XFS_DQ_GROUP: case XFS_DQ_GROUP:
case XFS_DQ_PROJ:
return &qi->qi_gquota_tree; return &qi->qi_gquota_tree;
case XFS_DQ_PROJ:
return &qi->qi_pquota_tree;
default: default:
ASSERT(0); ASSERT(0);
} }
...@@ -93,8 +96,9 @@ xfs_dq_to_quota_inode(struct xfs_dquot *dqp) ...@@ -93,8 +96,9 @@ xfs_dq_to_quota_inode(struct xfs_dquot *dqp)
case XFS_DQ_USER: case XFS_DQ_USER:
return dqp->q_mount->m_quotainfo->qi_uquotaip; return dqp->q_mount->m_quotainfo->qi_uquotaip;
case XFS_DQ_GROUP: case XFS_DQ_GROUP:
case XFS_DQ_PROJ:
return dqp->q_mount->m_quotainfo->qi_gquotaip; return dqp->q_mount->m_quotainfo->qi_gquotaip;
case XFS_DQ_PROJ:
return dqp->q_mount->m_quotainfo->qi_pquotaip;
default: default:
ASSERT(0); ASSERT(0);
} }
...@@ -107,18 +111,20 @@ extern void xfs_trans_mod_dquot(struct xfs_trans *, ...@@ -107,18 +111,20 @@ extern void xfs_trans_mod_dquot(struct xfs_trans *,
struct xfs_dquot *, uint, long); struct xfs_dquot *, uint, long);
extern int xfs_trans_reserve_quota_bydquots(struct xfs_trans *, extern int xfs_trans_reserve_quota_bydquots(struct xfs_trans *,
struct xfs_mount *, struct xfs_dquot *, struct xfs_mount *, struct xfs_dquot *,
struct xfs_dquot *, long, long, uint); struct xfs_dquot *, struct xfs_dquot *,
long, long, uint);
extern void xfs_trans_dqjoin(struct xfs_trans *, struct xfs_dquot *); extern void xfs_trans_dqjoin(struct xfs_trans *, struct xfs_dquot *);
extern void xfs_trans_log_dquot(struct xfs_trans *, struct xfs_dquot *); extern void xfs_trans_log_dquot(struct xfs_trans *, struct xfs_dquot *);
/* /*
* We keep the usr and grp dquots separately so that locking will be easier * We keep the usr, grp, and prj dquots separately so that locking will be
* to do at commit time. All transactions that we know of at this point * easier to do at commit time. All transactions that we know of at this point
* affect no more than two dquots of one type. Hence, the TRANS_MAXDQS value. * affect no more than two dquots of one type. Hence, the TRANS_MAXDQS value.
*/ */
enum { enum {
XFS_QM_TRANS_USR = 0, XFS_QM_TRANS_USR = 0,
XFS_QM_TRANS_GRP, XFS_QM_TRANS_GRP,
XFS_QM_TRANS_PRJ,
XFS_QM_TRANS_DQTYPES XFS_QM_TRANS_DQTYPES
}; };
#define XFS_QM_TRANS_MAXDQS 2 #define XFS_QM_TRANS_MAXDQS 2
......
...@@ -112,16 +112,16 @@ xfs_qm_newmount( ...@@ -112,16 +112,16 @@ xfs_qm_newmount(
if (((uquotaondisk && !XFS_IS_UQUOTA_ON(mp)) || if (((uquotaondisk && !XFS_IS_UQUOTA_ON(mp)) ||
(!uquotaondisk && XFS_IS_UQUOTA_ON(mp)) || (!uquotaondisk && XFS_IS_UQUOTA_ON(mp)) ||
(pquotaondisk && !XFS_IS_PQUOTA_ON(mp)) ||
(!pquotaondisk && XFS_IS_PQUOTA_ON(mp)) ||
(gquotaondisk && !XFS_IS_GQUOTA_ON(mp)) || (gquotaondisk && !XFS_IS_GQUOTA_ON(mp)) ||
(!gquotaondisk && XFS_IS_OQUOTA_ON(mp))) && (!gquotaondisk && XFS_IS_GQUOTA_ON(mp)) ||
(pquotaondisk && !XFS_IS_PQUOTA_ON(mp)) ||
(!pquotaondisk && XFS_IS_PQUOTA_ON(mp))) &&
xfs_dev_is_read_only(mp, "changing quota state")) { xfs_dev_is_read_only(mp, "changing quota state")) {
xfs_warn(mp, "please mount with%s%s%s%s.", xfs_warn(mp, "please mount with%s%s%s%s.",
(!quotaondisk ? "out quota" : ""), (!quotaondisk ? "out quota" : ""),
(uquotaondisk ? " usrquota" : ""), (uquotaondisk ? " usrquota" : ""),
(pquotaondisk ? " prjquota" : ""), (gquotaondisk ? " grpquota" : ""),
(gquotaondisk ? " grpquota" : "")); (pquotaondisk ? " prjquota" : ""));
return XFS_ERROR(EPERM); return XFS_ERROR(EPERM);
} }
......
...@@ -119,7 +119,8 @@ xfs_qm_scall_quotaoff( ...@@ -119,7 +119,8 @@ xfs_qm_scall_quotaoff(
dqtype |= XFS_QMOPT_GQUOTA; dqtype |= XFS_QMOPT_GQUOTA;
flags |= (XFS_GQUOTA_CHKD | XFS_GQUOTA_ENFD); flags |= (XFS_GQUOTA_CHKD | XFS_GQUOTA_ENFD);
inactivate_flags |= XFS_GQUOTA_ACTIVE; inactivate_flags |= XFS_GQUOTA_ACTIVE;
} else if (flags & XFS_PQUOTA_ACCT) { }
if (flags & XFS_PQUOTA_ACCT) {
dqtype |= XFS_QMOPT_PQUOTA; dqtype |= XFS_QMOPT_PQUOTA;
flags |= (XFS_PQUOTA_CHKD | XFS_PQUOTA_ENFD); flags |= (XFS_PQUOTA_CHKD | XFS_PQUOTA_ENFD);
inactivate_flags |= XFS_PQUOTA_ACTIVE; inactivate_flags |= XFS_PQUOTA_ACTIVE;
...@@ -198,10 +199,9 @@ xfs_qm_scall_quotaoff( ...@@ -198,10 +199,9 @@ xfs_qm_scall_quotaoff(
} }
/* /*
* If quotas is completely disabled, close shop. * If all quotas are completely turned off, close shop.
*/ */
if (((flags & XFS_MOUNT_QUOTA_ALL) == XFS_MOUNT_QUOTA_SET1) || if (mp->m_qflags == 0) {
((flags & XFS_MOUNT_QUOTA_ALL) == XFS_MOUNT_QUOTA_SET2)) {
mutex_unlock(&q->qi_quotaofflock); mutex_unlock(&q->qi_quotaofflock);
xfs_qm_destroy_quotainfo(mp); xfs_qm_destroy_quotainfo(mp);
return (0); return (0);
...@@ -214,10 +214,14 @@ xfs_qm_scall_quotaoff( ...@@ -214,10 +214,14 @@ xfs_qm_scall_quotaoff(
IRELE(q->qi_uquotaip); IRELE(q->qi_uquotaip);
q->qi_uquotaip = NULL; q->qi_uquotaip = NULL;
} }
if ((dqtype & (XFS_QMOPT_GQUOTA|XFS_QMOPT_PQUOTA)) && q->qi_gquotaip) { if ((dqtype & XFS_QMOPT_GQUOTA) && q->qi_gquotaip) {
IRELE(q->qi_gquotaip); IRELE(q->qi_gquotaip);
q->qi_gquotaip = NULL; q->qi_gquotaip = NULL;
} }
if ((dqtype & XFS_QMOPT_PQUOTA) && q->qi_pquotaip) {
IRELE(q->qi_pquotaip);
q->qi_pquotaip = NULL;
}
out_unlock: out_unlock:
mutex_unlock(&q->qi_quotaofflock); mutex_unlock(&q->qi_quotaofflock);
...@@ -859,9 +863,11 @@ xfs_dqrele_inode( ...@@ -859,9 +863,11 @@ xfs_dqrele_inode(
{ {
/* skip quota inodes */ /* skip quota inodes */
if (ip == ip->i_mount->m_quotainfo->qi_uquotaip || if (ip == ip->i_mount->m_quotainfo->qi_uquotaip ||
ip == ip->i_mount->m_quotainfo->qi_gquotaip) { ip == ip->i_mount->m_quotainfo->qi_gquotaip ||
ip == ip->i_mount->m_quotainfo->qi_pquotaip) {
ASSERT(ip->i_udquot == NULL); ASSERT(ip->i_udquot == NULL);
ASSERT(ip->i_gdquot == NULL); ASSERT(ip->i_gdquot == NULL);
ASSERT(ip->i_pdquot == NULL);
return 0; return 0;
} }
...@@ -870,10 +876,14 @@ xfs_dqrele_inode( ...@@ -870,10 +876,14 @@ xfs_dqrele_inode(
xfs_qm_dqrele(ip->i_udquot); xfs_qm_dqrele(ip->i_udquot);
ip->i_udquot = NULL; ip->i_udquot = NULL;
} }
if (flags & (XFS_PQUOTA_ACCT|XFS_GQUOTA_ACCT) && ip->i_gdquot) { if ((flags & XFS_GQUOTA_ACCT) && ip->i_gdquot) {
xfs_qm_dqrele(ip->i_gdquot); xfs_qm_dqrele(ip->i_gdquot);
ip->i_gdquot = NULL; ip->i_gdquot = NULL;
} }
if ((flags & XFS_PQUOTA_ACCT) && ip->i_pdquot) {
xfs_qm_dqrele(ip->i_pdquot);
ip->i_pdquot = NULL;
}
xfs_iunlock(ip, XFS_ILOCK_EXCL); xfs_iunlock(ip, XFS_ILOCK_EXCL);
return 0; return 0;
} }
......
...@@ -108,11 +108,28 @@ typedef struct xfs_dqblk { ...@@ -108,11 +108,28 @@ typedef struct xfs_dqblk {
{ XFS_DQ_FREEING, "FREEING" } { XFS_DQ_FREEING, "FREEING" }
/* /*
* In the worst case, when both user and group quotas are on, * We have the possibility of all three quota types being active at once, and
* we can have a max of three dquots changing in a single transaction. * hence free space modification requires modification of all three current
* dquots in a single transaction. For this case we need to have a reservation
* of at least 3 dquots.
*
* However, a chmod operation can change both UID and GID in a single
* transaction, resulting in requiring {old, new} x {uid, gid} dquots to be
* modified. Hence for this case we need to reserve space for at least 4 dquots.
*
* And in the worst case, there's a rename operation that can be modifying up to
* 4 inodes with dquots attached to them. In reality, the only inodes that can
* have their dquots modified are the source and destination directory inodes
* due to directory name creation and removal. That can require space allocation
* and/or freeing on both directory inodes, and hence all three dquots on each
* inode can be modified. And if the directories are world writeable, all the
* dquots can be unique and so 6 dquots can be modified....
*
* And, of course, we also need to take into account the dquot log format item
* used to describe each dquot.
*/ */
#define XFS_DQUOT_LOGRES(mp) (sizeof(xfs_disk_dquot_t) * 3) #define XFS_DQUOT_LOGRES(mp) \
((sizeof(struct xfs_dq_logformat) + sizeof(struct xfs_disk_dquot)) * 6)
/* /*
* These are the structures used to lay out dquots and quotaoff * These are the structures used to lay out dquots and quotaoff
...@@ -271,10 +288,10 @@ typedef struct xfs_qoff_logformat { ...@@ -271,10 +288,10 @@ typedef struct xfs_qoff_logformat {
* we didn't have the inode locked, the appropriate dquot(s) will be * we didn't have the inode locked, the appropriate dquot(s) will be
* attached atomically. * attached atomically.
*/ */
#define XFS_NOT_DQATTACHED(mp, ip) ((XFS_IS_UQUOTA_ON(mp) &&\ #define XFS_NOT_DQATTACHED(mp, ip) \
(ip)->i_udquot == NULL) || \ ((XFS_IS_UQUOTA_ON(mp) && (ip)->i_udquot == NULL) || \
(XFS_IS_OQUOTA_ON(mp) && \ (XFS_IS_GQUOTA_ON(mp) && (ip)->i_gdquot == NULL) || \
(ip)->i_gdquot == NULL)) (XFS_IS_PQUOTA_ON(mp) && (ip)->i_pdquot == NULL))
#define XFS_QM_NEED_QUOTACHECK(mp) \ #define XFS_QM_NEED_QUOTACHECK(mp) \
((XFS_IS_UQUOTA_ON(mp) && \ ((XFS_IS_UQUOTA_ON(mp) && \
...@@ -284,14 +301,6 @@ typedef struct xfs_qoff_logformat { ...@@ -284,14 +301,6 @@ typedef struct xfs_qoff_logformat {
(XFS_IS_PQUOTA_ON(mp) && \ (XFS_IS_PQUOTA_ON(mp) && \
(mp->m_sb.sb_qflags & XFS_PQUOTA_CHKD) == 0)) (mp->m_sb.sb_qflags & XFS_PQUOTA_CHKD) == 0))
#define XFS_MOUNT_QUOTA_SET1 (XFS_UQUOTA_ACCT|XFS_UQUOTA_ENFD|\
XFS_UQUOTA_CHKD|XFS_GQUOTA_ACCT|\
XFS_GQUOTA_ENFD|XFS_GQUOTA_CHKD)
#define XFS_MOUNT_QUOTA_SET2 (XFS_UQUOTA_ACCT|XFS_UQUOTA_ENFD|\
XFS_UQUOTA_CHKD|XFS_PQUOTA_ACCT|\
XFS_PQUOTA_ENFD|XFS_PQUOTA_CHKD)
#define XFS_MOUNT_QUOTA_ALL (XFS_UQUOTA_ACCT|XFS_UQUOTA_ENFD|\ #define XFS_MOUNT_QUOTA_ALL (XFS_UQUOTA_ACCT|XFS_UQUOTA_ENFD|\
XFS_UQUOTA_CHKD|XFS_GQUOTA_ACCT|\ XFS_UQUOTA_CHKD|XFS_GQUOTA_ACCT|\
XFS_GQUOTA_ENFD|XFS_GQUOTA_CHKD|\ XFS_GQUOTA_ENFD|XFS_GQUOTA_CHKD|\
...@@ -329,17 +338,18 @@ extern int xfs_trans_reserve_quota_nblks(struct xfs_trans *, ...@@ -329,17 +338,18 @@ extern int xfs_trans_reserve_quota_nblks(struct xfs_trans *,
struct xfs_inode *, long, long, uint); struct xfs_inode *, long, long, uint);
extern int xfs_trans_reserve_quota_bydquots(struct xfs_trans *, extern int xfs_trans_reserve_quota_bydquots(struct xfs_trans *,
struct xfs_mount *, struct xfs_dquot *, struct xfs_mount *, struct xfs_dquot *,
struct xfs_dquot *, long, long, uint); struct xfs_dquot *, struct xfs_dquot *, long, long, uint);
extern int xfs_qm_vop_dqalloc(struct xfs_inode *, uid_t, gid_t, prid_t, uint, extern int xfs_qm_vop_dqalloc(struct xfs_inode *, uid_t, gid_t, prid_t, uint,
struct xfs_dquot **, struct xfs_dquot **); struct xfs_dquot **, struct xfs_dquot **, struct xfs_dquot **);
extern void xfs_qm_vop_create_dqattach(struct xfs_trans *, struct xfs_inode *, extern void xfs_qm_vop_create_dqattach(struct xfs_trans *, struct xfs_inode *,
struct xfs_dquot *, struct xfs_dquot *); struct xfs_dquot *, struct xfs_dquot *, struct xfs_dquot *);
extern int xfs_qm_vop_rename_dqattach(struct xfs_inode **); extern int xfs_qm_vop_rename_dqattach(struct xfs_inode **);
extern struct xfs_dquot *xfs_qm_vop_chown(struct xfs_trans *, extern struct xfs_dquot *xfs_qm_vop_chown(struct xfs_trans *,
struct xfs_inode *, struct xfs_dquot **, struct xfs_dquot *); struct xfs_inode *, struct xfs_dquot **, struct xfs_dquot *);
extern int xfs_qm_vop_chown_reserve(struct xfs_trans *, struct xfs_inode *, extern int xfs_qm_vop_chown_reserve(struct xfs_trans *, struct xfs_inode *,
struct xfs_dquot *, struct xfs_dquot *, uint); struct xfs_dquot *, struct xfs_dquot *,
struct xfs_dquot *, uint);
extern int xfs_qm_dqattach(struct xfs_inode *, uint); extern int xfs_qm_dqattach(struct xfs_inode *, uint);
extern int xfs_qm_dqattach_locked(struct xfs_inode *, uint); extern int xfs_qm_dqattach_locked(struct xfs_inode *, uint);
extern void xfs_qm_dqdetach(struct xfs_inode *); extern void xfs_qm_dqdetach(struct xfs_inode *);
...@@ -353,10 +363,12 @@ extern void xfs_qm_unmount_quotas(struct xfs_mount *); ...@@ -353,10 +363,12 @@ extern void xfs_qm_unmount_quotas(struct xfs_mount *);
#else #else
static inline int static inline int
xfs_qm_vop_dqalloc(struct xfs_inode *ip, uid_t uid, gid_t gid, prid_t prid, xfs_qm_vop_dqalloc(struct xfs_inode *ip, uid_t uid, gid_t gid, prid_t prid,
uint flags, struct xfs_dquot **udqp, struct xfs_dquot **gdqp) uint flags, struct xfs_dquot **udqp, struct xfs_dquot **gdqp,
struct xfs_dquot **pdqp)
{ {
*udqp = NULL; *udqp = NULL;
*gdqp = NULL; *gdqp = NULL;
*pdqp = NULL;
return 0; return 0;
} }
#define xfs_trans_dup_dqinfo(tp, tp2) #define xfs_trans_dup_dqinfo(tp, tp2)
...@@ -371,14 +383,15 @@ static inline int xfs_trans_reserve_quota_nblks(struct xfs_trans *tp, ...@@ -371,14 +383,15 @@ static inline int xfs_trans_reserve_quota_nblks(struct xfs_trans *tp,
} }
static inline int xfs_trans_reserve_quota_bydquots(struct xfs_trans *tp, static inline int xfs_trans_reserve_quota_bydquots(struct xfs_trans *tp,
struct xfs_mount *mp, struct xfs_dquot *udqp, struct xfs_mount *mp, struct xfs_dquot *udqp,
struct xfs_dquot *gdqp, long nblks, long nions, uint flags) struct xfs_dquot *gdqp, struct xfs_dquot *pdqp,
long nblks, long nions, uint flags)
{ {
return 0; return 0;
} }
#define xfs_qm_vop_create_dqattach(tp, ip, u, g) #define xfs_qm_vop_create_dqattach(tp, ip, u, g, p)
#define xfs_qm_vop_rename_dqattach(it) (0) #define xfs_qm_vop_rename_dqattach(it) (0)
#define xfs_qm_vop_chown(tp, ip, old, new) (NULL) #define xfs_qm_vop_chown(tp, ip, old, new) (NULL)
#define xfs_qm_vop_chown_reserve(tp, ip, u, g, fl) (0) #define xfs_qm_vop_chown_reserve(tp, ip, u, g, p, fl) (0)
#define xfs_qm_dqattach(ip, fl) (0) #define xfs_qm_dqattach(ip, fl) (0)
#define xfs_qm_dqattach_locked(ip, fl) (0) #define xfs_qm_dqattach_locked(ip, fl) (0)
#define xfs_qm_dqdetach(ip) #define xfs_qm_dqdetach(ip)
...@@ -392,8 +405,8 @@ static inline int xfs_trans_reserve_quota_bydquots(struct xfs_trans *tp, ...@@ -392,8 +405,8 @@ static inline int xfs_trans_reserve_quota_bydquots(struct xfs_trans *tp,
#define xfs_trans_unreserve_quota_nblks(tp, ip, nblks, ninos, flags) \ #define xfs_trans_unreserve_quota_nblks(tp, ip, nblks, ninos, flags) \
xfs_trans_reserve_quota_nblks(tp, ip, -(nblks), -(ninos), flags) xfs_trans_reserve_quota_nblks(tp, ip, -(nblks), -(ninos), flags)
#define xfs_trans_reserve_quota(tp, mp, ud, gd, nb, ni, f) \ #define xfs_trans_reserve_quota(tp, mp, ud, gd, pd, nb, ni, f) \
xfs_trans_reserve_quota_bydquots(tp, mp, ud, gd, nb, ni, \ xfs_trans_reserve_quota_bydquots(tp, mp, ud, gd, pd, nb, ni, \
f | XFS_QMOPT_RES_REGBLKS) f | XFS_QMOPT_RES_REGBLKS)
extern int xfs_qm_dqcheck(struct xfs_mount *, xfs_disk_dquot_t *, extern int xfs_qm_dqcheck(struct xfs_mount *, xfs_disk_dquot_t *,
......
...@@ -360,6 +360,7 @@ xfs_symlink( ...@@ -360,6 +360,7 @@ xfs_symlink(
prid_t prid; prid_t prid;
struct xfs_dquot *udqp = NULL; struct xfs_dquot *udqp = NULL;
struct xfs_dquot *gdqp = NULL; struct xfs_dquot *gdqp = NULL;
struct xfs_dquot *pdqp = NULL;
uint resblks; uint resblks;
*ipp = NULL; *ipp = NULL;
...@@ -386,7 +387,7 @@ xfs_symlink( ...@@ -386,7 +387,7 @@ xfs_symlink(
* Make sure that we have allocated dquot(s) on disk. * Make sure that we have allocated dquot(s) on disk.
*/ */
error = xfs_qm_vop_dqalloc(dp, current_fsuid(), current_fsgid(), prid, error = xfs_qm_vop_dqalloc(dp, current_fsuid(), current_fsgid(), prid,
XFS_QMOPT_QUOTALL | XFS_QMOPT_INHERIT, &udqp, &gdqp); XFS_QMOPT_QUOTALL | XFS_QMOPT_INHERIT, &udqp, &gdqp, &pdqp);
if (error) if (error)
goto std_return; goto std_return;
...@@ -427,7 +428,8 @@ xfs_symlink( ...@@ -427,7 +428,8 @@ xfs_symlink(
/* /*
* Reserve disk quota : blocks and inode. * Reserve disk quota : blocks and inode.
*/ */
error = xfs_trans_reserve_quota(tp, mp, udqp, gdqp, resblks, 1, 0); error = xfs_trans_reserve_quota(tp, mp, udqp, gdqp,
pdqp, resblks, 1, 0);
if (error) if (error)
goto error_return; goto error_return;
...@@ -465,7 +467,7 @@ xfs_symlink( ...@@ -465,7 +467,7 @@ xfs_symlink(
/* /*
* Also attach the dquot(s) to it, if applicable. * Also attach the dquot(s) to it, if applicable.
*/ */
xfs_qm_vop_create_dqattach(tp, ip, udqp, gdqp); xfs_qm_vop_create_dqattach(tp, ip, udqp, gdqp, pdqp);
if (resblks) if (resblks)
resblks -= XFS_IALLOC_SPACE_RES(mp); resblks -= XFS_IALLOC_SPACE_RES(mp);
...@@ -563,6 +565,7 @@ xfs_symlink( ...@@ -563,6 +565,7 @@ xfs_symlink(
error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES);
xfs_qm_dqrele(udqp); xfs_qm_dqrele(udqp);
xfs_qm_dqrele(gdqp); xfs_qm_dqrele(gdqp);
xfs_qm_dqrele(pdqp);
*ipp = ip; *ipp = ip;
return 0; return 0;
...@@ -576,6 +579,7 @@ xfs_symlink( ...@@ -576,6 +579,7 @@ xfs_symlink(
xfs_trans_cancel(tp, cancel_flags); xfs_trans_cancel(tp, cancel_flags);
xfs_qm_dqrele(udqp); xfs_qm_dqrele(udqp);
xfs_qm_dqrele(gdqp); xfs_qm_dqrele(gdqp);
xfs_qm_dqrele(pdqp);
if (unlock_dp_on_error) if (unlock_dp_on_error)
xfs_iunlock(dp, XFS_ILOCK_EXCL); xfs_iunlock(dp, XFS_ILOCK_EXCL);
......
...@@ -163,8 +163,10 @@ xfs_trans_mod_dquot_byino( ...@@ -163,8 +163,10 @@ xfs_trans_mod_dquot_byino(
if (XFS_IS_UQUOTA_ON(mp) && ip->i_udquot) if (XFS_IS_UQUOTA_ON(mp) && ip->i_udquot)
(void) xfs_trans_mod_dquot(tp, ip->i_udquot, field, delta); (void) xfs_trans_mod_dquot(tp, ip->i_udquot, field, delta);
if (XFS_IS_OQUOTA_ON(mp) && ip->i_gdquot) if (XFS_IS_GQUOTA_ON(mp) && ip->i_gdquot)
(void) xfs_trans_mod_dquot(tp, ip->i_gdquot, field, delta); (void) xfs_trans_mod_dquot(tp, ip->i_gdquot, field, delta);
if (XFS_IS_PQUOTA_ON(mp) && ip->i_pdquot)
(void) xfs_trans_mod_dquot(tp, ip->i_pdquot, field, delta);
} }
STATIC struct xfs_dqtrx * STATIC struct xfs_dqtrx *
...@@ -177,8 +179,12 @@ xfs_trans_get_dqtrx( ...@@ -177,8 +179,12 @@ xfs_trans_get_dqtrx(
if (XFS_QM_ISUDQ(dqp)) if (XFS_QM_ISUDQ(dqp))
qa = tp->t_dqinfo->dqs[XFS_QM_TRANS_USR]; qa = tp->t_dqinfo->dqs[XFS_QM_TRANS_USR];
else else if (XFS_QM_ISGDQ(dqp))
qa = tp->t_dqinfo->dqs[XFS_QM_TRANS_GRP]; qa = tp->t_dqinfo->dqs[XFS_QM_TRANS_GRP];
else if (XFS_QM_ISPDQ(dqp))
qa = tp->t_dqinfo->dqs[XFS_QM_TRANS_PRJ];
else
return NULL;
for (i = 0; i < XFS_QM_TRANS_MAXDQS; i++) { for (i = 0; i < XFS_QM_TRANS_MAXDQS; i++) {
if (qa[i].qt_dquot == NULL || if (qa[i].qt_dquot == NULL ||
...@@ -291,11 +297,10 @@ xfs_trans_mod_dquot( ...@@ -291,11 +297,10 @@ xfs_trans_mod_dquot(
/* /*
* Given an array of dqtrx structures, lock all the dquots associated * Given an array of dqtrx structures, lock all the dquots associated and join
* and join them to the transaction, provided they have been modified. * them to the transaction, provided they have been modified. We know that the
* We know that the highest number of dquots (of one type - usr OR grp), * highest number of dquots of one type - usr, grp OR prj - involved in a
* involved in a transaction is 2 and that both usr and grp combined - 3. * transaction is 2 so we don't need to make this very generic.
* So, we don't attempt to make this very generic.
*/ */
STATIC void STATIC void
xfs_trans_dqlockedjoin( xfs_trans_dqlockedjoin(
...@@ -728,8 +733,8 @@ xfs_trans_dqresv( ...@@ -728,8 +733,8 @@ xfs_trans_dqresv(
/* /*
* Given dquot(s), make disk block and/or inode reservations against them. * Given dquot(s), make disk block and/or inode reservations against them.
* The fact that this does the reservation against both the usr and * The fact that this does the reservation against user, group and
* grp/prj quotas is important, because this follows a both-or-nothing * project quotas is important, because this follows a all-or-nothing
* approach. * approach.
* *
* flags = XFS_QMOPT_FORCE_RES evades limit enforcement. Used by chown. * flags = XFS_QMOPT_FORCE_RES evades limit enforcement. Used by chown.
...@@ -744,6 +749,7 @@ xfs_trans_reserve_quota_bydquots( ...@@ -744,6 +749,7 @@ xfs_trans_reserve_quota_bydquots(
struct xfs_mount *mp, struct xfs_mount *mp,
struct xfs_dquot *udqp, struct xfs_dquot *udqp,
struct xfs_dquot *gdqp, struct xfs_dquot *gdqp,
struct xfs_dquot *pdqp,
long nblks, long nblks,
long ninos, long ninos,
uint flags) uint flags)
...@@ -771,11 +777,21 @@ xfs_trans_reserve_quota_bydquots( ...@@ -771,11 +777,21 @@ xfs_trans_reserve_quota_bydquots(
goto unwind_usr; goto unwind_usr;
} }
if (pdqp) {
error = xfs_trans_dqresv(tp, mp, pdqp, nblks, ninos, flags);
if (error)
goto unwind_grp;
}
/* /*
* Didn't change anything critical, so, no need to log * Didn't change anything critical, so, no need to log
*/ */
return 0; return 0;
unwind_grp:
flags |= XFS_QMOPT_FORCE_RES;
if (gdqp)
xfs_trans_dqresv(tp, mp, gdqp, -nblks, -ninos, flags);
unwind_usr: unwind_usr:
flags |= XFS_QMOPT_FORCE_RES; flags |= XFS_QMOPT_FORCE_RES;
if (udqp) if (udqp)
...@@ -817,6 +833,7 @@ xfs_trans_reserve_quota_nblks( ...@@ -817,6 +833,7 @@ xfs_trans_reserve_quota_nblks(
*/ */
return xfs_trans_reserve_quota_bydquots(tp, mp, return xfs_trans_reserve_quota_bydquots(tp, mp,
ip->i_udquot, ip->i_gdquot, ip->i_udquot, ip->i_gdquot,
ip->i_pdquot,
nblks, ninos, flags); nblks, ninos, flags);
} }
......
...@@ -489,6 +489,7 @@ xfs_create( ...@@ -489,6 +489,7 @@ xfs_create(
prid_t prid; prid_t prid;
struct xfs_dquot *udqp = NULL; struct xfs_dquot *udqp = NULL;
struct xfs_dquot *gdqp = NULL; struct xfs_dquot *gdqp = NULL;
struct xfs_dquot *pdqp = NULL;
uint resblks; uint resblks;
uint log_res; uint log_res;
uint log_count; uint log_count;
...@@ -507,7 +508,8 @@ xfs_create( ...@@ -507,7 +508,8 @@ xfs_create(
* Make sure that we have allocated dquot(s) on disk. * Make sure that we have allocated dquot(s) on disk.
*/ */
error = xfs_qm_vop_dqalloc(dp, current_fsuid(), current_fsgid(), prid, error = xfs_qm_vop_dqalloc(dp, current_fsuid(), current_fsgid(), prid,
XFS_QMOPT_QUOTALL | XFS_QMOPT_INHERIT, &udqp, &gdqp); XFS_QMOPT_QUOTALL | XFS_QMOPT_INHERIT,
&udqp, &gdqp, &pdqp);
if (error) if (error)
return error; return error;
...@@ -559,7 +561,8 @@ xfs_create( ...@@ -559,7 +561,8 @@ xfs_create(
/* /*
* Reserve disk quota and the inode. * Reserve disk quota and the inode.
*/ */
error = xfs_trans_reserve_quota(tp, mp, udqp, gdqp, resblks, 1, 0); error = xfs_trans_reserve_quota(tp, mp, udqp, gdqp,
pdqp, resblks, 1, 0);
if (error) if (error)
goto out_trans_cancel; goto out_trans_cancel;
...@@ -623,7 +626,7 @@ xfs_create( ...@@ -623,7 +626,7 @@ xfs_create(
* These ids of the inode couldn't have changed since the new * These ids of the inode couldn't have changed since the new
* inode has been locked ever since it was created. * inode has been locked ever since it was created.
*/ */
xfs_qm_vop_create_dqattach(tp, ip, udqp, gdqp); xfs_qm_vop_create_dqattach(tp, ip, udqp, gdqp, pdqp);
error = xfs_bmap_finish(&tp, &free_list, &committed); error = xfs_bmap_finish(&tp, &free_list, &committed);
if (error) if (error)
...@@ -635,6 +638,7 @@ xfs_create( ...@@ -635,6 +638,7 @@ xfs_create(
xfs_qm_dqrele(udqp); xfs_qm_dqrele(udqp);
xfs_qm_dqrele(gdqp); xfs_qm_dqrele(gdqp);
xfs_qm_dqrele(pdqp);
*ipp = ip; *ipp = ip;
return 0; return 0;
...@@ -656,6 +660,7 @@ xfs_create( ...@@ -656,6 +660,7 @@ xfs_create(
xfs_qm_dqrele(udqp); xfs_qm_dqrele(udqp);
xfs_qm_dqrele(gdqp); xfs_qm_dqrele(gdqp);
xfs_qm_dqrele(pdqp);
if (unlock_dp_on_error) if (unlock_dp_on_error)
xfs_iunlock(dp, XFS_ILOCK_EXCL); xfs_iunlock(dp, XFS_ILOCK_EXCL);
...@@ -1568,7 +1573,7 @@ xfs_free_file_space( ...@@ -1568,7 +1573,7 @@ xfs_free_file_space(
} }
xfs_ilock(ip, XFS_ILOCK_EXCL); xfs_ilock(ip, XFS_ILOCK_EXCL);
error = xfs_trans_reserve_quota(tp, mp, error = xfs_trans_reserve_quota(tp, mp,
ip->i_udquot, ip->i_gdquot, ip->i_udquot, ip->i_gdquot, ip->i_pdquot,
resblks, 0, XFS_QMOPT_RES_REGBLKS); resblks, 0, XFS_QMOPT_RES_REGBLKS);
if (error) if (error)
goto error1; goto error1;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment