xfs: update for 3.20-rc1
This update contains: o RENAME_EXCHANGE support o Rework of the superblock logging infrastructure o Rework of the XFS_IOCTL_SETXATTR implementation - enables use inside user namespaces - fixes inconsistencies setting extent size hints o fixes for missing buffer type annotations used in log recovery o more consolidation of libxfs headers o preparation patches for block based PNFS support o miscellaneous bug fixes and cleanups -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iQIcBAABAgAGBQJU2UQ6AAoJEK3oKUf0dfodNAwQAKD2T154VZCXZtjC6LobdEL4 tuk72eDEyduNQZ1yL8MPNf090lsFizoLmXocawQEKPHZnSnZu1E6VQLughI5wSge N658/5mNvuRQkvUTw13onPhNdOzSoqigiy+6u0qiZbhab72ZExfuloqAAgZ908Ak DWZRQNXDspG8olGD1EKyEOKAoWDgRljiBEUw7+wVAN2thdQRXs6pyyTcaIXst3j6 bk9EAeWZOK78OJUjtWlvIFv7RqjjUQL/8Z4Arr3lMuv8Y1fxS3oL8V9GjKae0MUn ZFPJzRHE9kfcO4AYAwYDUDW0Ia4ccq3sYu8FDzX6FTf3AqziQXKW/ovw83So5oEE vpT/ltsugWfu2feShvfa9FTYxqmecXUqoITZ4Sczm/8F9tDz0Q4HZ/GxleAmJyH9 89IbTlSgm6qy1nmSbg21x0CVuOqJqHFAG83nnMKv8X4gmaMg9SzdNlU1iL2ugBQ7 l2XKS2DKDbjgBnSdW+bOdBoFiMyCUjrwMAp23vYELuTIFSdHkTFsBQBvqtpjkJ0X KEutMaZdy8uo5DdPRN9k78OANbzOc4qYKJHTjiO5sQsMybsPNkciLBX49CgYFmw4 xoVQ+EYrebpBKi4WHyLKWFOWb5CP/AUtLgxsX0clQVBuuaEBhA306qic1ZZixtIm ea2Q8ODEgpzs8QWZ7j6+ =eZEU -----END PGP SIGNATURE----- Merge tag 'xfs-for-linus-3.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/dgc/linux-xfs Pull xfs update from Dave Chinner: "This update contains: - RENAME_EXCHANGE support - Rework of the superblock logging infrastructure - Rework of the XFS_IOCTL_SETXATTR implementation * enables use inside user namespaces * fixes inconsistencies setting extent size hints - fixes for missing buffer type annotations used in log recovery - more consolidation of libxfs headers - preparation patches for block based PNFS support - miscellaneous bug fixes and cleanups" * tag 'xfs-for-linus-3.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/dgc/linux-xfs: (37 commits) xfs: only trace buffer items if they exist xfs: report proper f_files in statfs if we overshoot imaxpct xfs: fix panic_mask documentation xfs: xfs_ioctl_setattr_check_projid can be static xfs: growfs should use synchronous transactions xfs: fix behaviour of XFS_IOC_FSSETXATTR on directories xfs: factor projid hint checking out of xfs_ioctl_setattr xfs: factor extsize hint checking out of xfs_ioctl_setattr xfs: XFS_IOCTL_SETXATTR can run in user namespaces xfs: kill xfs_ioctl_setattr behaviour mask xfs: disaggregate xfs_ioctl_setattr xfs: factor out xfs_ioctl_setattr transaciton preamble xfs: separate xflags from xfs_ioctl_setattr xfs: FSX_NONBLOCK is not used xfs: don't allocate an ioend for direct I/O completions xfs: change kmem_free to use generic kvfree() xfs: factor out a xfs_update_prealloc_flags() helper xfs: remove incorrect error negation in attr_multi ioctl xfs: set superblock buffer type correctly xfs: set buf types when converting extent formats ...
This commit is contained in:
commit
ae90fb1420
|
@ -287,9 +287,9 @@ The following sysctls are available for the XFS filesystem:
|
|||
XFS_ERRLEVEL_LOW: 1
|
||||
XFS_ERRLEVEL_HIGH: 5
|
||||
|
||||
fs.xfs.panic_mask (Min: 0 Default: 0 Max: 127)
|
||||
fs.xfs.panic_mask (Min: 0 Default: 0 Max: 255)
|
||||
Causes certain error conditions to call BUG(). Value is a bitmask;
|
||||
AND together the tags which represent errors which should cause panics:
|
||||
OR together the tags which represent errors which should cause panics:
|
||||
|
||||
XFS_NO_PTAG 0
|
||||
XFS_PTAG_IFLUSH 0x00000001
|
||||
|
@ -299,6 +299,7 @@ The following sysctls are available for the XFS filesystem:
|
|||
XFS_PTAG_SHUTDOWN_CORRUPT 0x00000010
|
||||
XFS_PTAG_SHUTDOWN_IOERROR 0x00000020
|
||||
XFS_PTAG_SHUTDOWN_LOGERROR 0x00000040
|
||||
XFS_PTAG_FSBLOCK_ZERO 0x00000080
|
||||
|
||||
This option is intended for debugging only.
|
||||
|
||||
|
@ -348,16 +349,13 @@ The following sysctls are available for the XFS filesystem:
|
|||
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.
|
||||
None at present.
|
||||
|
||||
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.
|
||||
Removed Sysctls
|
||||
===============
|
||||
|
||||
Due for removal in 3.14.
|
||||
Name Removed
|
||||
---- -------
|
||||
fs.xfs.xfsbufd_centisec v3.20
|
||||
fs.xfs.age_buffer_centisecs v3.20
|
||||
|
|
|
@ -91,16 +91,6 @@ kmem_zalloc_large(size_t size, xfs_km_flags_t flags)
|
|||
return ptr;
|
||||
}
|
||||
|
||||
void
|
||||
kmem_free(const void *ptr)
|
||||
{
|
||||
if (!is_vmalloc_addr(ptr)) {
|
||||
kfree(ptr);
|
||||
} else {
|
||||
vfree(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void *
|
||||
kmem_realloc(const void *ptr, size_t newsize, size_t oldsize,
|
||||
xfs_km_flags_t flags)
|
||||
|
|
|
@ -63,7 +63,10 @@ kmem_flags_convert(xfs_km_flags_t flags)
|
|||
extern void *kmem_alloc(size_t, xfs_km_flags_t);
|
||||
extern void *kmem_zalloc_large(size_t size, xfs_km_flags_t);
|
||||
extern void *kmem_realloc(const void *, size_t, size_t, xfs_km_flags_t);
|
||||
extern void kmem_free(const void *);
|
||||
static inline void kmem_free(const void *ptr)
|
||||
{
|
||||
kvfree(ptr);
|
||||
}
|
||||
|
||||
|
||||
extern void *kmem_zalloc_greedy(size_t *, size_t, size_t);
|
||||
|
|
|
@ -403,7 +403,7 @@ xfs_sbversion_add_attr2(xfs_mount_t *mp, xfs_trans_t *tp)
|
|||
if (!xfs_sb_version_hasattr2(&mp->m_sb)) {
|
||||
xfs_sb_version_addattr2(&mp->m_sb);
|
||||
spin_unlock(&mp->m_sb_lock);
|
||||
xfs_mod_sb(tp, XFS_SB_VERSIONNUM | XFS_SB_FEATURES2);
|
||||
xfs_log_sb(tp);
|
||||
} else
|
||||
spin_unlock(&mp->m_sb_lock);
|
||||
}
|
||||
|
|
|
@ -973,7 +973,11 @@ xfs_bmap_local_to_extents(
|
|||
*firstblock = args.fsbno;
|
||||
bp = xfs_btree_get_bufl(args.mp, tp, args.fsbno, 0);
|
||||
|
||||
/* initialise the block and copy the data */
|
||||
/*
|
||||
* Initialise the block and copy the data
|
||||
*
|
||||
* Note: init_fn must set the buffer log item type correctly!
|
||||
*/
|
||||
init_fn(tp, bp, ip, ifp);
|
||||
|
||||
/* account for the change in fork size and log everything */
|
||||
|
@ -1221,22 +1225,20 @@ xfs_bmap_add_attrfork(
|
|||
goto bmap_cancel;
|
||||
if (!xfs_sb_version_hasattr(&mp->m_sb) ||
|
||||
(!xfs_sb_version_hasattr2(&mp->m_sb) && version == 2)) {
|
||||
__int64_t sbfields = 0;
|
||||
bool log_sb = false;
|
||||
|
||||
spin_lock(&mp->m_sb_lock);
|
||||
if (!xfs_sb_version_hasattr(&mp->m_sb)) {
|
||||
xfs_sb_version_addattr(&mp->m_sb);
|
||||
sbfields |= XFS_SB_VERSIONNUM;
|
||||
log_sb = true;
|
||||
}
|
||||
if (!xfs_sb_version_hasattr2(&mp->m_sb) && version == 2) {
|
||||
xfs_sb_version_addattr2(&mp->m_sb);
|
||||
sbfields |= (XFS_SB_VERSIONNUM | XFS_SB_FEATURES2);
|
||||
log_sb = true;
|
||||
}
|
||||
if (sbfields) {
|
||||
spin_unlock(&mp->m_sb_lock);
|
||||
xfs_mod_sb(tp, sbfields);
|
||||
} else
|
||||
spin_unlock(&mp->m_sb_lock);
|
||||
spin_unlock(&mp->m_sb_lock);
|
||||
if (log_sb)
|
||||
xfs_log_sb(tp);
|
||||
}
|
||||
|
||||
error = xfs_bmap_finish(&tp, &flist, &committed);
|
||||
|
|
|
@ -27,6 +27,37 @@ struct xfs_trans;
|
|||
|
||||
extern kmem_zone_t *xfs_bmap_free_item_zone;
|
||||
|
||||
/*
|
||||
* Argument structure for xfs_bmap_alloc.
|
||||
*/
|
||||
struct xfs_bmalloca {
|
||||
xfs_fsblock_t *firstblock; /* i/o first block allocated */
|
||||
struct xfs_bmap_free *flist; /* bmap freelist */
|
||||
struct xfs_trans *tp; /* transaction pointer */
|
||||
struct xfs_inode *ip; /* incore inode pointer */
|
||||
struct xfs_bmbt_irec prev; /* extent before the new one */
|
||||
struct xfs_bmbt_irec got; /* extent after, or delayed */
|
||||
|
||||
xfs_fileoff_t offset; /* offset in file filling in */
|
||||
xfs_extlen_t length; /* i/o length asked/allocated */
|
||||
xfs_fsblock_t blkno; /* starting block of new extent */
|
||||
|
||||
struct xfs_btree_cur *cur; /* btree cursor */
|
||||
xfs_extnum_t idx; /* current extent index */
|
||||
int nallocs;/* number of extents alloc'd */
|
||||
int logflags;/* flags for transaction logging */
|
||||
|
||||
xfs_extlen_t total; /* total blocks needed for xaction */
|
||||
xfs_extlen_t minlen; /* minimum allocation size (blocks) */
|
||||
xfs_extlen_t minleft; /* amount must be left after alloc */
|
||||
bool eof; /* set if allocating past last extent */
|
||||
bool wasdel; /* replacing a delayed allocation */
|
||||
bool userdata;/* set if is user data */
|
||||
bool aeof; /* allocated space at eof */
|
||||
bool conv; /* overwriting unwritten extents */
|
||||
int flags;
|
||||
};
|
||||
|
||||
/*
|
||||
* List of extents to be free "later".
|
||||
* The list is kept sorted on xbf_startblock.
|
||||
|
@ -149,6 +180,8 @@ 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,
|
||||
struct xfs_bmap_free *flist, struct xfs_mount *mp);
|
||||
void xfs_bmap_cancel(struct xfs_bmap_free *flist);
|
||||
int xfs_bmap_finish(struct xfs_trans **tp, struct xfs_bmap_free *flist,
|
||||
int *committed);
|
||||
void xfs_bmap_compute_maxlevels(struct xfs_mount *mp, int whichfork);
|
||||
int xfs_bmap_first_unused(struct xfs_trans *tp, struct xfs_inode *ip,
|
||||
xfs_extlen_t len, xfs_fileoff_t *unused, int whichfork);
|
||||
|
|
|
@ -151,10 +151,13 @@ typedef struct xfs_sb {
|
|||
__uint32_t sb_features2; /* additional feature bits */
|
||||
|
||||
/*
|
||||
* bad features2 field as a result of failing to pad the sb
|
||||
* structure to 64 bits. Some machines will be using this field
|
||||
* for features2 bits. Easiest just to mark it bad and not use
|
||||
* it for anything else.
|
||||
* bad features2 field as a result of failing to pad the sb structure to
|
||||
* 64 bits. Some machines will be using this field for features2 bits.
|
||||
* Easiest just to mark it bad and not use it for anything else.
|
||||
*
|
||||
* This is not kept up to date in memory; it is always overwritten by
|
||||
* the value in sb_features2 when formatting the incore superblock to
|
||||
* the disk buffer.
|
||||
*/
|
||||
__uint32_t sb_bad_features2;
|
||||
|
||||
|
@ -304,8 +307,8 @@ typedef enum {
|
|||
#define XFS_SB_ICOUNT XFS_SB_MVAL(ICOUNT)
|
||||
#define XFS_SB_IFREE XFS_SB_MVAL(IFREE)
|
||||
#define XFS_SB_FDBLOCKS XFS_SB_MVAL(FDBLOCKS)
|
||||
#define XFS_SB_FEATURES2 XFS_SB_MVAL(FEATURES2)
|
||||
#define XFS_SB_BAD_FEATURES2 XFS_SB_MVAL(BAD_FEATURES2)
|
||||
#define XFS_SB_FEATURES2 (XFS_SB_MVAL(FEATURES2) | \
|
||||
XFS_SB_MVAL(BAD_FEATURES2))
|
||||
#define XFS_SB_FEATURES_COMPAT XFS_SB_MVAL(FEATURES_COMPAT)
|
||||
#define XFS_SB_FEATURES_RO_COMPAT XFS_SB_MVAL(FEATURES_RO_COMPAT)
|
||||
#define XFS_SB_FEATURES_INCOMPAT XFS_SB_MVAL(FEATURES_INCOMPAT)
|
||||
|
@ -319,9 +322,9 @@ typedef enum {
|
|||
XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO | XFS_SB_GQUOTINO | \
|
||||
XFS_SB_QFLAGS | XFS_SB_SHARED_VN | XFS_SB_UNIT | XFS_SB_WIDTH | \
|
||||
XFS_SB_ICOUNT | XFS_SB_IFREE | XFS_SB_FDBLOCKS | XFS_SB_FEATURES2 | \
|
||||
XFS_SB_BAD_FEATURES2 | XFS_SB_FEATURES_COMPAT | \
|
||||
XFS_SB_FEATURES_RO_COMPAT | XFS_SB_FEATURES_INCOMPAT | \
|
||||
XFS_SB_FEATURES_LOG_INCOMPAT | XFS_SB_PQUOTINO)
|
||||
XFS_SB_FEATURES_COMPAT | XFS_SB_FEATURES_RO_COMPAT | \
|
||||
XFS_SB_FEATURES_INCOMPAT | XFS_SB_FEATURES_LOG_INCOMPAT | \
|
||||
XFS_SB_PQUOTINO)
|
||||
|
||||
|
||||
/*
|
||||
|
@ -453,13 +456,11 @@ static inline void xfs_sb_version_addattr2(struct xfs_sb *sbp)
|
|||
{
|
||||
sbp->sb_versionnum |= XFS_SB_VERSION_MOREBITSBIT;
|
||||
sbp->sb_features2 |= XFS_SB_VERSION2_ATTR2BIT;
|
||||
sbp->sb_bad_features2 |= XFS_SB_VERSION2_ATTR2BIT;
|
||||
}
|
||||
|
||||
static inline void xfs_sb_version_removeattr2(struct xfs_sb *sbp)
|
||||
{
|
||||
sbp->sb_features2 &= ~XFS_SB_VERSION2_ATTR2BIT;
|
||||
sbp->sb_bad_features2 &= ~XFS_SB_VERSION2_ATTR2BIT;
|
||||
if (!sbp->sb_features2)
|
||||
sbp->sb_versionnum &= ~XFS_SB_VERSION_MOREBITSBIT;
|
||||
}
|
||||
|
@ -475,7 +476,6 @@ static inline void xfs_sb_version_addprojid32bit(struct xfs_sb *sbp)
|
|||
{
|
||||
sbp->sb_versionnum |= XFS_SB_VERSION_MOREBITSBIT;
|
||||
sbp->sb_features2 |= XFS_SB_VERSION2_PROJID32BIT;
|
||||
sbp->sb_bad_features2 |= XFS_SB_VERSION2_PROJID32BIT;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -40,69 +40,6 @@
|
|||
* Physical superblock buffer manipulations. Shared with libxfs in userspace.
|
||||
*/
|
||||
|
||||
static const struct {
|
||||
short offset;
|
||||
short type; /* 0 = integer
|
||||
* 1 = binary / string (no translation)
|
||||
*/
|
||||
} xfs_sb_info[] = {
|
||||
{ offsetof(xfs_sb_t, sb_magicnum), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_blocksize), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_dblocks), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_rblocks), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_rextents), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_uuid), 1 },
|
||||
{ offsetof(xfs_sb_t, sb_logstart), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_rootino), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_rbmino), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_rsumino), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_rextsize), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_agblocks), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_agcount), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_rbmblocks), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_logblocks), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_versionnum), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_sectsize), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_inodesize), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_inopblock), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_fname[0]), 1 },
|
||||
{ offsetof(xfs_sb_t, sb_blocklog), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_sectlog), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_inodelog), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_inopblog), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_agblklog), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_rextslog), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_inprogress), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_imax_pct), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_icount), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_ifree), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_fdblocks), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_frextents), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_uquotino), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_gquotino), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_qflags), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_flags), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_shared_vn), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_inoalignmt), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_unit), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_width), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_dirblklog), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_logsectlog), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_logsectsize), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_logsunit), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_features2), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_bad_features2), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_features_compat), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_features_ro_compat), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_features_incompat), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_features_log_incompat), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_crc), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_pad), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_pquotino), 0 },
|
||||
{ offsetof(xfs_sb_t, sb_lsn), 0 },
|
||||
{ sizeof(xfs_sb_t), 0 }
|
||||
};
|
||||
|
||||
/*
|
||||
* Reference counting access wrappers to the perag structures.
|
||||
* Because we never free per-ag structures, the only thing we
|
||||
|
@ -461,58 +398,49 @@ xfs_sb_from_disk(
|
|||
__xfs_sb_from_disk(to, from, true);
|
||||
}
|
||||
|
||||
static inline void
|
||||
static void
|
||||
xfs_sb_quota_to_disk(
|
||||
xfs_dsb_t *to,
|
||||
xfs_sb_t *from,
|
||||
__int64_t *fields)
|
||||
struct xfs_dsb *to,
|
||||
struct xfs_sb *from)
|
||||
{
|
||||
__uint16_t qflags = from->sb_qflags;
|
||||
|
||||
/*
|
||||
* We need to do these manipilations only if we are working
|
||||
* with an older version of on-disk superblock.
|
||||
*/
|
||||
if (xfs_sb_version_has_pquotino(from))
|
||||
to->sb_uquotino = cpu_to_be64(from->sb_uquotino);
|
||||
if (xfs_sb_version_has_pquotino(from)) {
|
||||
to->sb_qflags = cpu_to_be16(from->sb_qflags);
|
||||
to->sb_gquotino = cpu_to_be64(from->sb_gquotino);
|
||||
to->sb_pquotino = cpu_to_be64(from->sb_pquotino);
|
||||
return;
|
||||
|
||||
if (*fields & XFS_SB_QFLAGS) {
|
||||
/*
|
||||
* The in-core version of sb_qflags do not have
|
||||
* XFS_OQUOTA_* flags, whereas the on-disk version
|
||||
* does. So, convert incore XFS_{PG}QUOTA_* flags
|
||||
* to on-disk XFS_OQUOTA_* flags.
|
||||
*/
|
||||
qflags &= ~(XFS_PQUOTA_ENFD | XFS_PQUOTA_CHKD |
|
||||
XFS_GQUOTA_ENFD | XFS_GQUOTA_CHKD);
|
||||
|
||||
if (from->sb_qflags &
|
||||
(XFS_PQUOTA_ENFD | XFS_GQUOTA_ENFD))
|
||||
qflags |= XFS_OQUOTA_ENFD;
|
||||
if (from->sb_qflags &
|
||||
(XFS_PQUOTA_CHKD | XFS_GQUOTA_CHKD))
|
||||
qflags |= XFS_OQUOTA_CHKD;
|
||||
to->sb_qflags = cpu_to_be16(qflags);
|
||||
*fields &= ~XFS_SB_QFLAGS;
|
||||
}
|
||||
|
||||
/*
|
||||
* GQUOTINO and PQUOTINO cannot be used together in versions of
|
||||
* superblock that do not have pquotino. from->sb_flags tells us which
|
||||
* quota is active and should be copied to disk. If neither are active,
|
||||
* make sure we write NULLFSINO to the sb_gquotino field as a quota
|
||||
* inode value of "0" is invalid when the XFS_SB_VERSION_QUOTA feature
|
||||
* bit is set.
|
||||
*
|
||||
* Note that we don't need to handle the sb_uquotino or sb_pquotino here
|
||||
* as they do not require any translation. Hence the main sb field loop
|
||||
* will write them appropriately from the in-core superblock.
|
||||
* The in-core version of sb_qflags do not have XFS_OQUOTA_*
|
||||
* flags, whereas the on-disk version does. So, convert incore
|
||||
* XFS_{PG}QUOTA_* flags to on-disk XFS_OQUOTA_* flags.
|
||||
*/
|
||||
if ((*fields & XFS_SB_GQUOTINO) &&
|
||||
(from->sb_qflags & XFS_GQUOTA_ACCT))
|
||||
qflags &= ~(XFS_PQUOTA_ENFD | XFS_PQUOTA_CHKD |
|
||||
XFS_GQUOTA_ENFD | XFS_GQUOTA_CHKD);
|
||||
|
||||
if (from->sb_qflags &
|
||||
(XFS_PQUOTA_ENFD | XFS_GQUOTA_ENFD))
|
||||
qflags |= XFS_OQUOTA_ENFD;
|
||||
if (from->sb_qflags &
|
||||
(XFS_PQUOTA_CHKD | XFS_GQUOTA_CHKD))
|
||||
qflags |= XFS_OQUOTA_CHKD;
|
||||
to->sb_qflags = cpu_to_be16(qflags);
|
||||
|
||||
/*
|
||||
* GQUOTINO and PQUOTINO cannot be used together in versions
|
||||
* of superblock that do not have pquotino. from->sb_flags
|
||||
* tells us which quota is active and should be copied to
|
||||
* disk. If neither are active, we should NULL the inode.
|
||||
*
|
||||
* In all cases, the separate pquotino must remain 0 because it
|
||||
* it beyond the "end" of the valid non-pquotino superblock.
|
||||
*/
|
||||
if (from->sb_qflags & XFS_GQUOTA_ACCT)
|
||||
to->sb_gquotino = cpu_to_be64(from->sb_gquotino);
|
||||
else if ((*fields & XFS_SB_PQUOTINO) &&
|
||||
(from->sb_qflags & XFS_PQUOTA_ACCT))
|
||||
else if (from->sb_qflags & XFS_PQUOTA_ACCT)
|
||||
to->sb_gquotino = cpu_to_be64(from->sb_pquotino);
|
||||
else {
|
||||
/*
|
||||
|
@ -526,63 +454,78 @@ xfs_sb_quota_to_disk(
|
|||
to->sb_gquotino = cpu_to_be64(NULLFSINO);
|
||||
}
|
||||
|
||||
*fields &= ~(XFS_SB_PQUOTINO | XFS_SB_GQUOTINO);
|
||||
to->sb_pquotino = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy in core superblock to ondisk one.
|
||||
*
|
||||
* The fields argument is mask of superblock fields to copy.
|
||||
*/
|
||||
void
|
||||
xfs_sb_to_disk(
|
||||
xfs_dsb_t *to,
|
||||
xfs_sb_t *from,
|
||||
__int64_t fields)
|
||||
struct xfs_dsb *to,
|
||||
struct xfs_sb *from)
|
||||
{
|
||||
xfs_caddr_t to_ptr = (xfs_caddr_t)to;
|
||||
xfs_caddr_t from_ptr = (xfs_caddr_t)from;
|
||||
xfs_sb_field_t f;
|
||||
int first;
|
||||
int size;
|
||||
xfs_sb_quota_to_disk(to, from);
|
||||
|
||||
ASSERT(fields);
|
||||
if (!fields)
|
||||
return;
|
||||
to->sb_magicnum = cpu_to_be32(from->sb_magicnum);
|
||||
to->sb_blocksize = cpu_to_be32(from->sb_blocksize);
|
||||
to->sb_dblocks = cpu_to_be64(from->sb_dblocks);
|
||||
to->sb_rblocks = cpu_to_be64(from->sb_rblocks);
|
||||
to->sb_rextents = cpu_to_be64(from->sb_rextents);
|
||||
memcpy(&to->sb_uuid, &from->sb_uuid, sizeof(to->sb_uuid));
|
||||
to->sb_logstart = cpu_to_be64(from->sb_logstart);
|
||||
to->sb_rootino = cpu_to_be64(from->sb_rootino);
|
||||
to->sb_rbmino = cpu_to_be64(from->sb_rbmino);
|
||||
to->sb_rsumino = cpu_to_be64(from->sb_rsumino);
|
||||
to->sb_rextsize = cpu_to_be32(from->sb_rextsize);
|
||||
to->sb_agblocks = cpu_to_be32(from->sb_agblocks);
|
||||
to->sb_agcount = cpu_to_be32(from->sb_agcount);
|
||||
to->sb_rbmblocks = cpu_to_be32(from->sb_rbmblocks);
|
||||
to->sb_logblocks = cpu_to_be32(from->sb_logblocks);
|
||||
to->sb_versionnum = cpu_to_be16(from->sb_versionnum);
|
||||
to->sb_sectsize = cpu_to_be16(from->sb_sectsize);
|
||||
to->sb_inodesize = cpu_to_be16(from->sb_inodesize);
|
||||
to->sb_inopblock = cpu_to_be16(from->sb_inopblock);
|
||||
memcpy(&to->sb_fname, &from->sb_fname, sizeof(to->sb_fname));
|
||||
to->sb_blocklog = from->sb_blocklog;
|
||||
to->sb_sectlog = from->sb_sectlog;
|
||||
to->sb_inodelog = from->sb_inodelog;
|
||||
to->sb_inopblog = from->sb_inopblog;
|
||||
to->sb_agblklog = from->sb_agblklog;
|
||||
to->sb_rextslog = from->sb_rextslog;
|
||||
to->sb_inprogress = from->sb_inprogress;
|
||||
to->sb_imax_pct = from->sb_imax_pct;
|
||||
to->sb_icount = cpu_to_be64(from->sb_icount);
|
||||
to->sb_ifree = cpu_to_be64(from->sb_ifree);
|
||||
to->sb_fdblocks = cpu_to_be64(from->sb_fdblocks);
|
||||
to->sb_frextents = cpu_to_be64(from->sb_frextents);
|
||||
|
||||
/* We should never write the crc here, it's updated in the IO path */
|
||||
fields &= ~XFS_SB_CRC;
|
||||
to->sb_flags = from->sb_flags;
|
||||
to->sb_shared_vn = from->sb_shared_vn;
|
||||
to->sb_inoalignmt = cpu_to_be32(from->sb_inoalignmt);
|
||||
to->sb_unit = cpu_to_be32(from->sb_unit);
|
||||
to->sb_width = cpu_to_be32(from->sb_width);
|
||||
to->sb_dirblklog = from->sb_dirblklog;
|
||||
to->sb_logsectlog = from->sb_logsectlog;
|
||||
to->sb_logsectsize = cpu_to_be16(from->sb_logsectsize);
|
||||
to->sb_logsunit = cpu_to_be32(from->sb_logsunit);
|
||||
|
||||
xfs_sb_quota_to_disk(to, from, &fields);
|
||||
while (fields) {
|
||||
f = (xfs_sb_field_t)xfs_lowbit64((__uint64_t)fields);
|
||||
first = xfs_sb_info[f].offset;
|
||||
size = xfs_sb_info[f + 1].offset - first;
|
||||
/*
|
||||
* We need to ensure that bad_features2 always matches features2.
|
||||
* Hence we enforce that here rather than having to remember to do it
|
||||
* everywhere else that updates features2.
|
||||
*/
|
||||
from->sb_bad_features2 = from->sb_features2;
|
||||
to->sb_features2 = cpu_to_be32(from->sb_features2);
|
||||
to->sb_bad_features2 = cpu_to_be32(from->sb_bad_features2);
|
||||
|
||||
ASSERT(xfs_sb_info[f].type == 0 || xfs_sb_info[f].type == 1);
|
||||
|
||||
if (size == 1 || xfs_sb_info[f].type == 1) {
|
||||
memcpy(to_ptr + first, from_ptr + first, size);
|
||||
} else {
|
||||
switch (size) {
|
||||
case 2:
|
||||
*(__be16 *)(to_ptr + first) =
|
||||
cpu_to_be16(*(__u16 *)(from_ptr + first));
|
||||
break;
|
||||
case 4:
|
||||
*(__be32 *)(to_ptr + first) =
|
||||
cpu_to_be32(*(__u32 *)(from_ptr + first));
|
||||
break;
|
||||
case 8:
|
||||
*(__be64 *)(to_ptr + first) =
|
||||
cpu_to_be64(*(__u64 *)(from_ptr + first));
|
||||
break;
|
||||
default:
|
||||
ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
fields &= ~(1LL << f);
|
||||
if (xfs_sb_version_hascrc(from)) {
|
||||
to->sb_features_compat = cpu_to_be32(from->sb_features_compat);
|
||||
to->sb_features_ro_compat =
|
||||
cpu_to_be32(from->sb_features_ro_compat);
|
||||
to->sb_features_incompat =
|
||||
cpu_to_be32(from->sb_features_incompat);
|
||||
to->sb_features_log_incompat =
|
||||
cpu_to_be32(from->sb_features_log_incompat);
|
||||
to->sb_pad = 0;
|
||||
to->sb_lsn = cpu_to_be64(from->sb_lsn);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -816,42 +759,51 @@ xfs_initialize_perag_data(
|
|||
}
|
||||
|
||||
/*
|
||||
* xfs_mod_sb() can be used to copy arbitrary changes to the
|
||||
* in-core superblock into the superblock buffer to be logged.
|
||||
* It does not provide the higher level of locking that is
|
||||
* needed to protect the in-core superblock from concurrent
|
||||
* access.
|
||||
* xfs_log_sb() can be used to copy arbitrary changes to the in-core superblock
|
||||
* into the superblock buffer to be logged. It does not provide the higher
|
||||
* level of locking that is needed to protect the in-core superblock from
|
||||
* concurrent access.
|
||||
*/
|
||||
void
|
||||
xfs_mod_sb(xfs_trans_t *tp, __int64_t fields)
|
||||
xfs_log_sb(
|
||||
struct xfs_trans *tp)
|
||||
{
|
||||
xfs_buf_t *bp;
|
||||
int first;
|
||||
int last;
|
||||
xfs_mount_t *mp;
|
||||
xfs_sb_field_t f;
|
||||
|
||||
ASSERT(fields);
|
||||
if (!fields)
|
||||
return;
|
||||
mp = tp->t_mountp;
|
||||
bp = xfs_trans_getsb(tp, mp, 0);
|
||||
first = sizeof(xfs_sb_t);
|
||||
last = 0;
|
||||
|
||||
/* translate/copy */
|
||||
|
||||
xfs_sb_to_disk(XFS_BUF_TO_SBP(bp), &mp->m_sb, fields);
|
||||
|
||||
/* find modified range */
|
||||
f = (xfs_sb_field_t)xfs_highbit64((__uint64_t)fields);
|
||||
ASSERT((1LL << f) & XFS_SB_MOD_BITS);
|
||||
last = xfs_sb_info[f + 1].offset - 1;
|
||||
|
||||
f = (xfs_sb_field_t)xfs_lowbit64((__uint64_t)fields);
|
||||
ASSERT((1LL << f) & XFS_SB_MOD_BITS);
|
||||
first = xfs_sb_info[f].offset;
|
||||
struct xfs_mount *mp = tp->t_mountp;
|
||||
struct xfs_buf *bp = xfs_trans_getsb(tp, mp, 0);
|
||||
|
||||
xfs_sb_to_disk(XFS_BUF_TO_SBP(bp), &mp->m_sb);
|
||||
xfs_trans_buf_set_type(tp, bp, XFS_BLFT_SB_BUF);
|
||||
xfs_trans_log_buf(tp, bp, first, last);
|
||||
xfs_trans_log_buf(tp, bp, 0, sizeof(struct xfs_dsb));
|
||||
}
|
||||
|
||||
/*
|
||||
* xfs_sync_sb
|
||||
*
|
||||
* Sync the superblock to disk.
|
||||
*
|
||||
* Note that the caller is responsible for checking the frozen state of the
|
||||
* filesystem. This procedure uses the non-blocking transaction allocator and
|
||||
* thus will allow modifications to a frozen fs. This is required because this
|
||||
* code can be called during the process of freezing where use of the high-level
|
||||
* allocator would deadlock.
|
||||
*/
|
||||
int
|
||||
xfs_sync_sb(
|
||||
struct xfs_mount *mp,
|
||||
bool wait)
|
||||
{
|
||||
struct xfs_trans *tp;
|
||||
int error;
|
||||
|
||||
tp = _xfs_trans_alloc(mp, XFS_TRANS_SB_CHANGE, KM_SLEEP);
|
||||
error = xfs_trans_reserve(tp, &M_RES(mp)->tr_sb, 0, 0);
|
||||
if (error) {
|
||||
xfs_trans_cancel(tp, 0);
|
||||
return error;
|
||||
}
|
||||
|
||||
xfs_log_sb(tp);
|
||||
if (wait)
|
||||
xfs_trans_set_sync(tp);
|
||||
return xfs_trans_commit(tp, 0);
|
||||
}
|
||||
|
|
|
@ -27,11 +27,12 @@ extern struct xfs_perag *xfs_perag_get_tag(struct xfs_mount *, xfs_agnumber_t,
|
|||
extern void xfs_perag_put(struct xfs_perag *pag);
|
||||
extern int xfs_initialize_perag_data(struct xfs_mount *, xfs_agnumber_t);
|
||||
|
||||
extern void xfs_sb_calc_crc(struct xfs_buf *);
|
||||
extern void xfs_mod_sb(struct xfs_trans *, __int64_t);
|
||||
extern void xfs_sb_mount_common(struct xfs_mount *, struct xfs_sb *);
|
||||
extern void xfs_sb_from_disk(struct xfs_sb *, struct xfs_dsb *);
|
||||
extern void xfs_sb_to_disk(struct xfs_dsb *, struct xfs_sb *, __int64_t);
|
||||
extern void xfs_sb_calc_crc(struct xfs_buf *bp);
|
||||
extern void xfs_log_sb(struct xfs_trans *tp);
|
||||
extern int xfs_sync_sb(struct xfs_mount *mp, bool wait);
|
||||
extern void xfs_sb_mount_common(struct xfs_mount *mp, struct xfs_sb *sbp);
|
||||
extern void xfs_sb_from_disk(struct xfs_sb *to, struct xfs_dsb *from);
|
||||
extern void xfs_sb_to_disk(struct xfs_dsb *to, struct xfs_sb *from);
|
||||
extern void xfs_sb_quota_from_disk(struct xfs_sb *sbp);
|
||||
|
||||
#endif /* __XFS_SB_H__ */
|
||||
|
|
|
@ -82,7 +82,7 @@ extern const struct xfs_buf_ops xfs_symlink_buf_ops;
|
|||
#define XFS_TRANS_ATTR_RM 23
|
||||
#define XFS_TRANS_ATTR_FLAG 24
|
||||
#define XFS_TRANS_CLEAR_AGI_BUCKET 25
|
||||
#define XFS_TRANS_QM_SBCHANGE 26
|
||||
#define XFS_TRANS_SB_CHANGE 26
|
||||
/*
|
||||
* Dummy entries since we use the transaction type to index into the
|
||||
* trans_type[] in xlog_recover_print_trans_head()
|
||||
|
@ -95,17 +95,15 @@ extern const struct xfs_buf_ops xfs_symlink_buf_ops;
|
|||
#define XFS_TRANS_QM_DQCLUSTER 32
|
||||
#define XFS_TRANS_QM_QINOCREATE 33
|
||||
#define XFS_TRANS_QM_QUOTAOFF_END 34
|
||||
#define XFS_TRANS_SB_UNIT 35
|
||||
#define XFS_TRANS_FSYNC_TS 36
|
||||
#define XFS_TRANS_GROWFSRT_ALLOC 37
|
||||
#define XFS_TRANS_GROWFSRT_ZERO 38
|
||||
#define XFS_TRANS_GROWFSRT_FREE 39
|
||||
#define XFS_TRANS_SWAPEXT 40
|
||||
#define XFS_TRANS_SB_COUNT 41
|
||||
#define XFS_TRANS_CHECKPOINT 42
|
||||
#define XFS_TRANS_ICREATE 43
|
||||
#define XFS_TRANS_CREATE_TMPFILE 44
|
||||
#define XFS_TRANS_TYPE_MAX 44
|
||||
#define XFS_TRANS_FSYNC_TS 35
|
||||
#define XFS_TRANS_GROWFSRT_ALLOC 36
|
||||
#define XFS_TRANS_GROWFSRT_ZERO 37
|
||||
#define XFS_TRANS_GROWFSRT_FREE 38
|
||||
#define XFS_TRANS_SWAPEXT 39
|
||||
#define XFS_TRANS_CHECKPOINT 40
|
||||
#define XFS_TRANS_ICREATE 41
|
||||
#define XFS_TRANS_CREATE_TMPFILE 42
|
||||
#define XFS_TRANS_TYPE_MAX 43
|
||||
/* new transaction types need to be reflected in xfs_logprint(8) */
|
||||
|
||||
#define XFS_TRANS_TYPES \
|
||||
|
@ -113,7 +111,6 @@ extern const struct xfs_buf_ops xfs_symlink_buf_ops;
|
|||
{ XFS_TRANS_SETATTR_SIZE, "SETATTR_SIZE" }, \
|
||||
{ XFS_TRANS_INACTIVE, "INACTIVE" }, \
|
||||
{ XFS_TRANS_CREATE, "CREATE" }, \
|
||||
{ XFS_TRANS_CREATE_TMPFILE, "CREATE_TMPFILE" }, \
|
||||
{ XFS_TRANS_CREATE_TRUNC, "CREATE_TRUNC" }, \
|
||||
{ XFS_TRANS_TRUNCATE_FILE, "TRUNCATE_FILE" }, \
|
||||
{ XFS_TRANS_REMOVE, "REMOVE" }, \
|
||||
|
@ -134,23 +131,23 @@ extern const struct xfs_buf_ops xfs_symlink_buf_ops;
|
|||
{ XFS_TRANS_ATTR_RM, "ATTR_RM" }, \
|
||||
{ XFS_TRANS_ATTR_FLAG, "ATTR_FLAG" }, \
|
||||
{ XFS_TRANS_CLEAR_AGI_BUCKET, "CLEAR_AGI_BUCKET" }, \
|
||||
{ XFS_TRANS_QM_SBCHANGE, "QM_SBCHANGE" }, \
|
||||
{ XFS_TRANS_SB_CHANGE, "SBCHANGE" }, \
|
||||
{ XFS_TRANS_DUMMY1, "DUMMY1" }, \
|
||||
{ XFS_TRANS_DUMMY2, "DUMMY2" }, \
|
||||
{ XFS_TRANS_QM_QUOTAOFF, "QM_QUOTAOFF" }, \
|
||||
{ XFS_TRANS_QM_DQALLOC, "QM_DQALLOC" }, \
|
||||
{ XFS_TRANS_QM_SETQLIM, "QM_SETQLIM" }, \
|
||||
{ XFS_TRANS_QM_DQCLUSTER, "QM_DQCLUSTER" }, \
|
||||
{ XFS_TRANS_QM_QINOCREATE, "QM_QINOCREATE" }, \
|
||||
{ XFS_TRANS_QM_QUOTAOFF_END, "QM_QOFF_END" }, \
|
||||
{ XFS_TRANS_SB_UNIT, "SB_UNIT" }, \
|
||||
{ XFS_TRANS_FSYNC_TS, "FSYNC_TS" }, \
|
||||
{ XFS_TRANS_GROWFSRT_ALLOC, "GROWFSRT_ALLOC" }, \
|
||||
{ XFS_TRANS_GROWFSRT_ZERO, "GROWFSRT_ZERO" }, \
|
||||
{ XFS_TRANS_GROWFSRT_FREE, "GROWFSRT_FREE" }, \
|
||||
{ XFS_TRANS_SWAPEXT, "SWAPEXT" }, \
|
||||
{ XFS_TRANS_SB_COUNT, "SB_COUNT" }, \
|
||||
{ XFS_TRANS_CHECKPOINT, "CHECKPOINT" }, \
|
||||
{ XFS_TRANS_DUMMY1, "DUMMY1" }, \
|
||||
{ XFS_TRANS_DUMMY2, "DUMMY2" }, \
|
||||
{ XFS_TRANS_ICREATE, "ICREATE" }, \
|
||||
{ XFS_TRANS_CREATE_TMPFILE, "CREATE_TMPFILE" }, \
|
||||
{ XLOG_UNMOUNT_REC_TYPE, "UNMOUNT" }
|
||||
|
||||
/*
|
||||
|
|
|
@ -178,6 +178,8 @@ xfs_symlink_local_to_remote(
|
|||
struct xfs_mount *mp = ip->i_mount;
|
||||
char *buf;
|
||||
|
||||
xfs_trans_buf_set_type(tp, bp, XFS_BLFT_SYMLINK_BUF);
|
||||
|
||||
if (!xfs_sb_version_hascrc(&mp->m_sb)) {
|
||||
bp->b_ops = NULL;
|
||||
memcpy(bp->b_addr, ifp->if_u1.if_data, ifp->if_bytes);
|
||||
|
|
|
@ -715,17 +715,6 @@ xfs_calc_clear_agi_bucket_reservation(
|
|||
return xfs_calc_buf_res(1, mp->m_sb.sb_sectsize);
|
||||
}
|
||||
|
||||
/*
|
||||
* Clearing the quotaflags in the superblock.
|
||||
* the super block for changing quota flags: sector size
|
||||
*/
|
||||
STATIC uint
|
||||
xfs_calc_qm_sbchange_reservation(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
return xfs_calc_buf_res(1, mp->m_sb.sb_sectsize);
|
||||
}
|
||||
|
||||
/*
|
||||
* Adjusting quota limits.
|
||||
* the xfs_disk_dquot_t: sizeof(struct xfs_disk_dquot)
|
||||
|
@ -864,9 +853,6 @@ xfs_trans_resv_calc(
|
|||
* The following transactions are logged in logical format with
|
||||
* a default log count.
|
||||
*/
|
||||
resp->tr_qm_sbchange.tr_logres = xfs_calc_qm_sbchange_reservation(mp);
|
||||
resp->tr_qm_sbchange.tr_logcount = XFS_DEFAULT_LOG_COUNT;
|
||||
|
||||
resp->tr_qm_setqlim.tr_logres = xfs_calc_qm_setqlim_reservation(mp);
|
||||
resp->tr_qm_setqlim.tr_logcount = XFS_DEFAULT_LOG_COUNT;
|
||||
|
||||
|
|
|
@ -56,7 +56,6 @@ struct xfs_trans_resv {
|
|||
struct xfs_trans_res tr_growrtalloc; /* grow realtime allocations */
|
||||
struct xfs_trans_res tr_growrtzero; /* grow realtime zeroing */
|
||||
struct xfs_trans_res tr_growrtfree; /* grow realtime freeing */
|
||||
struct xfs_trans_res tr_qm_sbchange; /* change quota flags */
|
||||
struct xfs_trans_res tr_qm_setqlim; /* adjust quota limits */
|
||||
struct xfs_trans_res tr_qm_dqalloc; /* allocate quota on disk */
|
||||
struct xfs_trans_res tr_qm_quotaoff; /* turn quota off */
|
||||
|
|
|
@ -135,30 +135,22 @@ xfs_setfilesize_trans_alloc(
|
|||
*/
|
||||
STATIC int
|
||||
xfs_setfilesize(
|
||||
struct xfs_ioend *ioend)
|
||||
struct xfs_inode *ip,
|
||||
struct xfs_trans *tp,
|
||||
xfs_off_t offset,
|
||||
size_t size)
|
||||
{
|
||||
struct xfs_inode *ip = XFS_I(ioend->io_inode);
|
||||
struct xfs_trans *tp = ioend->io_append_trans;
|
||||
xfs_fsize_t isize;
|
||||
|
||||
/*
|
||||
* The transaction may have been allocated in the I/O submission thread,
|
||||
* thus we need to mark ourselves as beeing in a transaction manually.
|
||||
* Similarly for freeze protection.
|
||||
*/
|
||||
current_set_flags_nested(&tp->t_pflags, PF_FSTRANS);
|
||||
rwsem_acquire_read(&VFS_I(ip)->i_sb->s_writers.lock_map[SB_FREEZE_FS-1],
|
||||
0, 1, _THIS_IP_);
|
||||
|
||||
xfs_ilock(ip, XFS_ILOCK_EXCL);
|
||||
isize = xfs_new_eof(ip, ioend->io_offset + ioend->io_size);
|
||||
isize = xfs_new_eof(ip, offset + size);
|
||||
if (!isize) {
|
||||
xfs_iunlock(ip, XFS_ILOCK_EXCL);
|
||||
xfs_trans_cancel(tp, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
trace_xfs_setfilesize(ip, ioend->io_offset, ioend->io_size);
|
||||
trace_xfs_setfilesize(ip, offset, size);
|
||||
|
||||
ip->i_d.di_size = isize;
|
||||
xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
|
||||
|
@ -167,6 +159,25 @@ xfs_setfilesize(
|
|||
return xfs_trans_commit(tp, 0);
|
||||
}
|
||||
|
||||
STATIC int
|
||||
xfs_setfilesize_ioend(
|
||||
struct xfs_ioend *ioend)
|
||||
{
|
||||
struct xfs_inode *ip = XFS_I(ioend->io_inode);
|
||||
struct xfs_trans *tp = ioend->io_append_trans;
|
||||
|
||||
/*
|
||||
* The transaction may have been allocated in the I/O submission thread,
|
||||
* thus we need to mark ourselves as being in a transaction manually.
|
||||
* Similarly for freeze protection.
|
||||
*/
|
||||
current_set_flags_nested(&tp->t_pflags, PF_FSTRANS);
|
||||
rwsem_acquire_read(&VFS_I(ip)->i_sb->s_writers.lock_map[SB_FREEZE_FS-1],
|
||||
0, 1, _THIS_IP_);
|
||||
|
||||
return xfs_setfilesize(ip, tp, ioend->io_offset, ioend->io_size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Schedule IO completion handling on the final put of an ioend.
|
||||
*
|
||||
|
@ -182,8 +193,7 @@ xfs_finish_ioend(
|
|||
|
||||
if (ioend->io_type == XFS_IO_UNWRITTEN)
|
||||
queue_work(mp->m_unwritten_workqueue, &ioend->io_work);
|
||||
else if (ioend->io_append_trans ||
|
||||
(ioend->io_isdirect && xfs_ioend_is_append(ioend)))
|
||||
else if (ioend->io_append_trans)
|
||||
queue_work(mp->m_data_workqueue, &ioend->io_work);
|
||||
else
|
||||
xfs_destroy_ioend(ioend);
|
||||
|
@ -215,22 +225,8 @@ xfs_end_io(
|
|||
if (ioend->io_type == XFS_IO_UNWRITTEN) {
|
||||
error = xfs_iomap_write_unwritten(ip, ioend->io_offset,
|
||||
ioend->io_size);
|
||||
} else if (ioend->io_isdirect && xfs_ioend_is_append(ioend)) {
|
||||
/*
|
||||
* For direct I/O we do not know if we need to allocate blocks
|
||||
* or not so we can't preallocate an append transaction as that
|
||||
* results in nested reservations and log space deadlocks. Hence
|
||||
* allocate the transaction here. While this is sub-optimal and
|
||||
* can block IO completion for some time, we're stuck with doing
|
||||
* it this way until we can pass the ioend to the direct IO
|
||||
* allocation callbacks and avoid nesting that way.
|
||||
*/
|
||||
error = xfs_setfilesize_trans_alloc(ioend);
|
||||
if (error)
|
||||
goto done;
|
||||
error = xfs_setfilesize(ioend);
|
||||
} else if (ioend->io_append_trans) {
|
||||
error = xfs_setfilesize(ioend);
|
||||
error = xfs_setfilesize_ioend(ioend);
|
||||
} else {
|
||||
ASSERT(!xfs_ioend_is_append(ioend));
|
||||
}
|
||||
|
@ -241,17 +237,6 @@ done:
|
|||
xfs_destroy_ioend(ioend);
|
||||
}
|
||||
|
||||
/*
|
||||
* Call IO completion handling in caller context on the final put of an ioend.
|
||||
*/
|
||||
STATIC void
|
||||
xfs_finish_ioend_sync(
|
||||
struct xfs_ioend *ioend)
|
||||
{
|
||||
if (atomic_dec_and_test(&ioend->io_remaining))
|
||||
xfs_end_io(&ioend->io_work);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate and initialise an IO completion structure.
|
||||
* We need to track unwritten extent write completion here initially.
|
||||
|
@ -273,7 +258,6 @@ xfs_alloc_ioend(
|
|||
* all the I/O from calling the completion routine too early.
|
||||
*/
|
||||
atomic_set(&ioend->io_remaining, 1);
|
||||
ioend->io_isdirect = 0;
|
||||
ioend->io_error = 0;
|
||||
ioend->io_list = NULL;
|
||||
ioend->io_type = type;
|
||||
|
@ -1459,11 +1443,7 @@ xfs_get_blocks_direct(
|
|||
*
|
||||
* If the private argument is non-NULL __xfs_get_blocks signals us that we
|
||||
* need to issue a transaction to convert the range from unwritten to written
|
||||
* extents. In case this is regular synchronous I/O we just call xfs_end_io
|
||||
* to do this and we are done. But in case this was a successful AIO
|
||||
* request this handler is called from interrupt context, from which we
|
||||
* can't start transactions. In that case offload the I/O completion to
|
||||
* the workqueues we also use for buffered I/O completion.
|
||||
* extents.
|
||||
*/
|
||||
STATIC void
|
||||
xfs_end_io_direct_write(
|
||||
|
@ -1472,7 +1452,12 @@ xfs_end_io_direct_write(
|
|||
ssize_t size,
|
||||
void *private)
|
||||
{
|
||||
struct xfs_ioend *ioend = iocb->private;
|
||||
struct inode *inode = file_inode(iocb->ki_filp);
|
||||
struct xfs_inode *ip = XFS_I(inode);
|
||||
struct xfs_mount *mp = ip->i_mount;
|
||||
|
||||
if (XFS_FORCED_SHUTDOWN(mp))
|
||||
return;
|
||||
|
||||
/*
|
||||
* While the generic direct I/O code updates the inode size, it does
|
||||
|
@ -1480,22 +1465,33 @@ xfs_end_io_direct_write(
|
|||
* end_io handler thinks the on-disk size is outside the in-core
|
||||
* size. To prevent this just update it a little bit earlier here.
|
||||
*/
|
||||
if (offset + size > i_size_read(ioend->io_inode))
|
||||
i_size_write(ioend->io_inode, offset + size);
|
||||
if (offset + size > i_size_read(inode))
|
||||
i_size_write(inode, offset + size);
|
||||
|
||||
/*
|
||||
* blockdev_direct_IO can return an error even after the I/O
|
||||
* completion handler was called. Thus we need to protect
|
||||
* against double-freeing.
|
||||
* For direct I/O we do not know if we need to allocate blocks or not,
|
||||
* so we can't preallocate an append transaction, as that results in
|
||||
* nested reservations and log space deadlocks. Hence allocate the
|
||||
* transaction here. While this is sub-optimal and can block IO
|
||||
* completion for some time, we're stuck with doing it this way until
|
||||
* we can pass the ioend to the direct IO allocation callbacks and
|
||||
* avoid nesting that way.
|
||||
*/
|
||||
iocb->private = NULL;
|
||||
if (private && size > 0) {
|
||||
xfs_iomap_write_unwritten(ip, offset, size);
|
||||
} else if (offset + size > ip->i_d.di_size) {
|
||||
struct xfs_trans *tp;
|
||||
int error;
|
||||
|
||||
ioend->io_offset = offset;
|
||||
ioend->io_size = size;
|
||||
if (private && size > 0)
|
||||
ioend->io_type = XFS_IO_UNWRITTEN;
|
||||
tp = xfs_trans_alloc(mp, XFS_TRANS_FSYNC_TS);
|
||||
error = xfs_trans_reserve(tp, &M_RES(mp)->tr_fsyncts, 0, 0);
|
||||
if (error) {
|
||||
xfs_trans_cancel(tp, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
xfs_finish_ioend_sync(ioend);
|
||||
xfs_setfilesize(ip, tp, offset, size);
|
||||
}
|
||||
}
|
||||
|
||||
STATIC ssize_t
|
||||
|
@ -1507,39 +1503,16 @@ xfs_vm_direct_IO(
|
|||
{
|
||||
struct inode *inode = iocb->ki_filp->f_mapping->host;
|
||||
struct block_device *bdev = xfs_find_bdev_for_inode(inode);
|
||||
struct xfs_ioend *ioend = NULL;
|
||||
ssize_t ret;
|
||||
|
||||
if (rw & WRITE) {
|
||||
size_t size = iov_iter_count(iter);
|
||||
|
||||
/*
|
||||
* We cannot preallocate a size update transaction here as we
|
||||
* don't know whether allocation is necessary or not. Hence we
|
||||
* can only tell IO completion that one is necessary if we are
|
||||
* not doing unwritten extent conversion.
|
||||
*/
|
||||
iocb->private = ioend = xfs_alloc_ioend(inode, XFS_IO_DIRECT);
|
||||
if (offset + size > XFS_I(inode)->i_d.di_size)
|
||||
ioend->io_isdirect = 1;
|
||||
|
||||
ret = __blockdev_direct_IO(rw, iocb, inode, bdev, iter,
|
||||
return __blockdev_direct_IO(rw, iocb, inode, bdev, iter,
|
||||
offset, xfs_get_blocks_direct,
|
||||
xfs_end_io_direct_write, NULL,
|
||||
DIO_ASYNC_EXTEND);
|
||||
if (ret != -EIOCBQUEUED && iocb->private)
|
||||
goto out_destroy_ioend;
|
||||
} else {
|
||||
ret = __blockdev_direct_IO(rw, iocb, inode, bdev, iter,
|
||||
offset, xfs_get_blocks_direct,
|
||||
NULL, NULL, 0);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
out_destroy_ioend:
|
||||
xfs_destroy_ioend(ioend);
|
||||
return ret;
|
||||
return __blockdev_direct_IO(rw, iocb, inode, bdev, iter,
|
||||
offset, xfs_get_blocks_direct,
|
||||
NULL, NULL, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -24,14 +24,12 @@ extern mempool_t *xfs_ioend_pool;
|
|||
* Types of I/O for bmap clustering and I/O completion tracking.
|
||||
*/
|
||||
enum {
|
||||
XFS_IO_DIRECT = 0, /* special case for direct I/O ioends */
|
||||
XFS_IO_DELALLOC, /* covers delalloc region */
|
||||
XFS_IO_UNWRITTEN, /* covers allocated but uninitialized data */
|
||||
XFS_IO_OVERWRITE, /* covers already allocated extent */
|
||||
};
|
||||
|
||||
#define XFS_IO_TYPES \
|
||||
{ 0, "" }, \
|
||||
{ XFS_IO_DELALLOC, "delalloc" }, \
|
||||
{ XFS_IO_UNWRITTEN, "unwritten" }, \
|
||||
{ XFS_IO_OVERWRITE, "overwrite" }
|
||||
|
@ -45,7 +43,6 @@ typedef struct xfs_ioend {
|
|||
unsigned int io_type; /* delalloc / unwritten */
|
||||
int io_error; /* I/O error code */
|
||||
atomic_t io_remaining; /* hold count */
|
||||
unsigned int io_isdirect : 1;/* direct I/O */
|
||||
struct inode *io_inode; /* file being written to */
|
||||
struct buffer_head *io_buffer_head;/* buffer linked list head */
|
||||
struct buffer_head *io_buffer_tail;/* buffer linked list tail */
|
||||
|
|
|
@ -26,43 +26,8 @@ struct xfs_ifork;
|
|||
struct xfs_inode;
|
||||
struct xfs_mount;
|
||||
struct xfs_trans;
|
||||
struct xfs_bmalloca;
|
||||
|
||||
/*
|
||||
* Argument structure for xfs_bmap_alloc.
|
||||
*/
|
||||
struct xfs_bmalloca {
|
||||
xfs_fsblock_t *firstblock; /* i/o first block allocated */
|
||||
struct xfs_bmap_free *flist; /* bmap freelist */
|
||||
struct xfs_trans *tp; /* transaction pointer */
|
||||
struct xfs_inode *ip; /* incore inode pointer */
|
||||
struct xfs_bmbt_irec prev; /* extent before the new one */
|
||||
struct xfs_bmbt_irec got; /* extent after, or delayed */
|
||||
|
||||
xfs_fileoff_t offset; /* offset in file filling in */
|
||||
xfs_extlen_t length; /* i/o length asked/allocated */
|
||||
xfs_fsblock_t blkno; /* starting block of new extent */
|
||||
|
||||
struct xfs_btree_cur *cur; /* btree cursor */
|
||||
xfs_extnum_t idx; /* current extent index */
|
||||
int nallocs;/* number of extents alloc'd */
|
||||
int logflags;/* flags for transaction logging */
|
||||
|
||||
xfs_extlen_t total; /* total blocks needed for xaction */
|
||||
xfs_extlen_t minlen; /* minimum allocation size (blocks) */
|
||||
xfs_extlen_t minleft; /* amount must be left after alloc */
|
||||
bool eof; /* set if allocating past last extent */
|
||||
bool wasdel; /* replacing a delayed allocation */
|
||||
bool userdata;/* set if is user data */
|
||||
bool aeof; /* allocated space at eof */
|
||||
bool conv; /* overwriting unwritten extents */
|
||||
int flags;
|
||||
struct completion *done;
|
||||
struct work_struct work;
|
||||
int result;
|
||||
};
|
||||
|
||||
int xfs_bmap_finish(struct xfs_trans **tp, struct xfs_bmap_free *flist,
|
||||
int *committed);
|
||||
int xfs_bmap_rtalloc(struct xfs_bmalloca *ap);
|
||||
int xfs_bmap_eof(struct xfs_inode *ip, xfs_fileoff_t endoff,
|
||||
int whichfork, int *eof);
|
||||
|
|
|
@ -319,6 +319,10 @@ xfs_buf_item_format(
|
|||
ASSERT(atomic_read(&bip->bli_refcount) > 0);
|
||||
ASSERT((bip->bli_flags & XFS_BLI_LOGGED) ||
|
||||
(bip->bli_flags & XFS_BLI_STALE));
|
||||
ASSERT((bip->bli_flags & XFS_BLI_STALE) ||
|
||||
(xfs_blft_from_flags(&bip->__bli_format) > XFS_BLFT_UNKNOWN_BUF
|
||||
&& xfs_blft_from_flags(&bip->__bli_format) < XFS_BLFT_MAX_BUF));
|
||||
|
||||
|
||||
/*
|
||||
* If it is an inode buffer, transfer the in-memory state to the
|
||||
|
@ -535,7 +539,7 @@ xfs_buf_item_push(
|
|||
if ((bp->b_flags & XBF_WRITE_FAIL) &&
|
||||
___ratelimit(&xfs_buf_write_fail_rl_state, "XFS:")) {
|
||||
xfs_warn(bp->b_target->bt_mount,
|
||||
"Detected failing async write on buffer block 0x%llx. Retrying async write.\n",
|
||||
"Detected failing async write on buffer block 0x%llx. Retrying async write.",
|
||||
(long long)bp->b_bn);
|
||||
}
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ static inline void xfs_dqflock(xfs_dquot_t *dqp)
|
|||
wait_for_completion(&dqp->q_flush);
|
||||
}
|
||||
|
||||
static inline int xfs_dqflock_nowait(xfs_dquot_t *dqp)
|
||||
static inline bool xfs_dqflock_nowait(xfs_dquot_t *dqp)
|
||||
{
|
||||
return try_wait_for_completion(&dqp->q_flush);
|
||||
}
|
||||
|
|
|
@ -127,6 +127,42 @@ xfs_iozero(
|
|||
return (-status);
|
||||
}
|
||||
|
||||
int
|
||||
xfs_update_prealloc_flags(
|
||||
struct xfs_inode *ip,
|
||||
enum xfs_prealloc_flags flags)
|
||||
{
|
||||
struct xfs_trans *tp;
|
||||
int error;
|
||||
|
||||
tp = xfs_trans_alloc(ip->i_mount, XFS_TRANS_WRITEID);
|
||||
error = xfs_trans_reserve(tp, &M_RES(ip->i_mount)->tr_writeid, 0, 0);
|
||||
if (error) {
|
||||
xfs_trans_cancel(tp, 0);
|
||||
return error;
|
||||
}
|
||||
|
||||
xfs_ilock(ip, XFS_ILOCK_EXCL);
|
||||
xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
|
||||
|
||||
if (!(flags & XFS_PREALLOC_INVISIBLE)) {
|
||||
ip->i_d.di_mode &= ~S_ISUID;
|
||||
if (ip->i_d.di_mode & S_IXGRP)
|
||||
ip->i_d.di_mode &= ~S_ISGID;
|
||||
xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
|
||||
}
|
||||
|
||||
if (flags & XFS_PREALLOC_SET)
|
||||
ip->i_d.di_flags |= XFS_DIFLAG_PREALLOC;
|
||||
if (flags & XFS_PREALLOC_CLEAR)
|
||||
ip->i_d.di_flags &= ~XFS_DIFLAG_PREALLOC;
|
||||
|
||||
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
|
||||
if (flags & XFS_PREALLOC_SYNC)
|
||||
xfs_trans_set_sync(tp);
|
||||
return xfs_trans_commit(tp, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fsync operations on directories are much simpler than on regular files,
|
||||
* as there is no file data to flush, and thus also no need for explicit
|
||||
|
@ -784,8 +820,8 @@ xfs_file_fallocate(
|
|||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
struct xfs_inode *ip = XFS_I(inode);
|
||||
struct xfs_trans *tp;
|
||||
long error;
|
||||
enum xfs_prealloc_flags flags = 0;
|
||||
loff_t new_size = 0;
|
||||
|
||||
if (!S_ISREG(inode->i_mode))
|
||||
|
@ -822,6 +858,8 @@ xfs_file_fallocate(
|
|||
if (error)
|
||||
goto out_unlock;
|
||||
} else {
|
||||
flags |= XFS_PREALLOC_SET;
|
||||
|
||||
if (!(mode & FALLOC_FL_KEEP_SIZE) &&
|
||||
offset + len > i_size_read(inode)) {
|
||||
new_size = offset + len;
|
||||
|
@ -839,28 +877,10 @@ xfs_file_fallocate(
|
|||
goto out_unlock;
|
||||
}
|
||||
|
||||
tp = xfs_trans_alloc(ip->i_mount, XFS_TRANS_WRITEID);
|
||||
error = xfs_trans_reserve(tp, &M_RES(ip->i_mount)->tr_writeid, 0, 0);
|
||||
if (error) {
|
||||
xfs_trans_cancel(tp, 0);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
xfs_ilock(ip, XFS_ILOCK_EXCL);
|
||||
xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
|
||||
ip->i_d.di_mode &= ~S_ISUID;
|
||||
if (ip->i_d.di_mode & S_IXGRP)
|
||||
ip->i_d.di_mode &= ~S_ISGID;
|
||||
|
||||
if (!(mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_COLLAPSE_RANGE)))
|
||||
ip->i_d.di_flags |= XFS_DIFLAG_PREALLOC;
|
||||
|
||||
xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
|
||||
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
|
||||
|
||||
if (file->f_flags & O_DSYNC)
|
||||
xfs_trans_set_sync(tp);
|
||||
error = xfs_trans_commit(tp, 0);
|
||||
flags |= XFS_PREALLOC_SYNC;
|
||||
|
||||
error = xfs_update_prealloc_flags(ip, flags);
|
||||
if (error)
|
||||
goto out_unlock;
|
||||
|
||||
|
|
|
@ -488,6 +488,7 @@ xfs_growfs_data_private(
|
|||
xfs_trans_mod_sb(tp, XFS_TRANS_SB_FDBLOCKS, nfree);
|
||||
if (dpct)
|
||||
xfs_trans_mod_sb(tp, XFS_TRANS_SB_IMAXPCT, dpct);
|
||||
xfs_trans_set_sync(tp);
|
||||
error = xfs_trans_commit(tp, 0);
|
||||
if (error)
|
||||
return error;
|
||||
|
@ -541,7 +542,7 @@ xfs_growfs_data_private(
|
|||
saved_error = error;
|
||||
continue;
|
||||
}
|
||||
xfs_sb_to_disk(XFS_BUF_TO_SBP(bp), &mp->m_sb, XFS_SB_ALL_BITS);
|
||||
xfs_sb_to_disk(XFS_BUF_TO_SBP(bp), &mp->m_sb);
|
||||
|
||||
error = xfs_bwrite(bp);
|
||||
xfs_buf_relse(bp);
|
||||
|
@ -756,37 +757,6 @@ out:
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Dump a transaction into the log that contains no real change. This is needed
|
||||
* to be able to make the log dirty or stamp the current tail LSN into the log
|
||||
* during the covering operation.
|
||||
*
|
||||
* We cannot use an inode here for this - that will push dirty state back up
|
||||
* into the VFS and then periodic inode flushing will prevent log covering from
|
||||
* making progress. Hence we log a field in the superblock instead and use a
|
||||
* synchronous transaction to ensure the superblock is immediately unpinned
|
||||
* and can be written back.
|
||||
*/
|
||||
int
|
||||
xfs_fs_log_dummy(
|
||||
xfs_mount_t *mp)
|
||||
{
|
||||
xfs_trans_t *tp;
|
||||
int error;
|
||||
|
||||
tp = _xfs_trans_alloc(mp, XFS_TRANS_DUMMY1, KM_SLEEP);
|
||||
error = xfs_trans_reserve(tp, &M_RES(mp)->tr_sb, 0, 0);
|
||||
if (error) {
|
||||
xfs_trans_cancel(tp, 0);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* log the UUID because it is an unchanging field */
|
||||
xfs_mod_sb(tp, XFS_SB_UUID);
|
||||
xfs_trans_set_sync(tp);
|
||||
return xfs_trans_commit(tp, 0);
|
||||
}
|
||||
|
||||
int
|
||||
xfs_fs_goingdown(
|
||||
xfs_mount_t *mp,
|
||||
|
|
|
@ -1995,6 +1995,7 @@ xfs_iunlink(
|
|||
agi->agi_unlinked[bucket_index] = cpu_to_be32(agino);
|
||||
offset = offsetof(xfs_agi_t, agi_unlinked) +
|
||||
(sizeof(xfs_agino_t) * bucket_index);
|
||||
xfs_trans_buf_set_type(tp, agibp, XFS_BLFT_AGI_BUF);
|
||||
xfs_trans_log_buf(tp, agibp, offset,
|
||||
(offset + sizeof(xfs_agino_t) - 1));
|
||||
return 0;
|
||||
|
@ -2086,6 +2087,7 @@ xfs_iunlink_remove(
|
|||
agi->agi_unlinked[bucket_index] = cpu_to_be32(next_agino);
|
||||
offset = offsetof(xfs_agi_t, agi_unlinked) +
|
||||
(sizeof(xfs_agino_t) * bucket_index);
|
||||
xfs_trans_buf_set_type(tp, agibp, XFS_BLFT_AGI_BUF);
|
||||
xfs_trans_log_buf(tp, agibp, offset,
|
||||
(offset + sizeof(xfs_agino_t) - 1));
|
||||
} else {
|
||||
|
@ -2655,6 +2657,124 @@ xfs_sort_for_rename(
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* xfs_cross_rename()
|
||||
*
|
||||
* responsible for handling RENAME_EXCHANGE flag in renameat2() sytemcall
|
||||
*/
|
||||
STATIC int
|
||||
xfs_cross_rename(
|
||||
struct xfs_trans *tp,
|
||||
struct xfs_inode *dp1,
|
||||
struct xfs_name *name1,
|
||||
struct xfs_inode *ip1,
|
||||
struct xfs_inode *dp2,
|
||||
struct xfs_name *name2,
|
||||
struct xfs_inode *ip2,
|
||||
struct xfs_bmap_free *free_list,
|
||||
xfs_fsblock_t *first_block,
|
||||
int spaceres)
|
||||
{
|
||||
int error = 0;
|
||||
int ip1_flags = 0;
|
||||
int ip2_flags = 0;
|
||||
int dp2_flags = 0;
|
||||
|
||||
/* Swap inode number for dirent in first parent */
|
||||
error = xfs_dir_replace(tp, dp1, name1,
|
||||
ip2->i_ino,
|
||||
first_block, free_list, spaceres);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
/* Swap inode number for dirent in second parent */
|
||||
error = xfs_dir_replace(tp, dp2, name2,
|
||||
ip1->i_ino,
|
||||
first_block, free_list, spaceres);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* If we're renaming one or more directories across different parents,
|
||||
* update the respective ".." entries (and link counts) to match the new
|
||||
* parents.
|
||||
*/
|
||||
if (dp1 != dp2) {
|
||||
dp2_flags = XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG;
|
||||
|
||||
if (S_ISDIR(ip2->i_d.di_mode)) {
|
||||
error = xfs_dir_replace(tp, ip2, &xfs_name_dotdot,
|
||||
dp1->i_ino, first_block,
|
||||
free_list, spaceres);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
/* transfer ip2 ".." reference to dp1 */
|
||||
if (!S_ISDIR(ip1->i_d.di_mode)) {
|
||||
error = xfs_droplink(tp, dp2);
|
||||
if (error)
|
||||
goto out;
|
||||
error = xfs_bumplink(tp, dp1);
|
||||
if (error)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Although ip1 isn't changed here, userspace needs
|
||||
* to be warned about the change, so that applications
|
||||
* relying on it (like backup ones), will properly
|
||||
* notify the change
|
||||
*/
|
||||
ip1_flags |= XFS_ICHGTIME_CHG;
|
||||
ip2_flags |= XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG;
|
||||
}
|
||||
|
||||
if (S_ISDIR(ip1->i_d.di_mode)) {
|
||||
error = xfs_dir_replace(tp, ip1, &xfs_name_dotdot,
|
||||
dp2->i_ino, first_block,
|
||||
free_list, spaceres);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
/* transfer ip1 ".." reference to dp2 */
|
||||
if (!S_ISDIR(ip2->i_d.di_mode)) {
|
||||
error = xfs_droplink(tp, dp1);
|
||||
if (error)
|
||||
goto out;
|
||||
error = xfs_bumplink(tp, dp2);
|
||||
if (error)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Although ip2 isn't changed here, userspace needs
|
||||
* to be warned about the change, so that applications
|
||||
* relying on it (like backup ones), will properly
|
||||
* notify the change
|
||||
*/
|
||||
ip1_flags |= XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG;
|
||||
ip2_flags |= XFS_ICHGTIME_CHG;
|
||||
}
|
||||
}
|
||||
|
||||
if (ip1_flags) {
|
||||
xfs_trans_ichgtime(tp, ip1, ip1_flags);
|
||||
xfs_trans_log_inode(tp, ip1, XFS_ILOG_CORE);
|
||||
}
|
||||
if (ip2_flags) {
|
||||
xfs_trans_ichgtime(tp, ip2, ip2_flags);
|
||||
xfs_trans_log_inode(tp, ip2, XFS_ILOG_CORE);
|
||||
}
|
||||
if (dp2_flags) {
|
||||
xfs_trans_ichgtime(tp, dp2, dp2_flags);
|
||||
xfs_trans_log_inode(tp, dp2, XFS_ILOG_CORE);
|
||||
}
|
||||
xfs_trans_ichgtime(tp, dp1, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
|
||||
xfs_trans_log_inode(tp, dp1, XFS_ILOG_CORE);
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* xfs_rename
|
||||
*/
|
||||
|
@ -2665,7 +2785,8 @@ xfs_rename(
|
|||
xfs_inode_t *src_ip,
|
||||
xfs_inode_t *target_dp,
|
||||
struct xfs_name *target_name,
|
||||
xfs_inode_t *target_ip)
|
||||
xfs_inode_t *target_ip,
|
||||
unsigned int flags)
|
||||
{
|
||||
xfs_trans_t *tp = NULL;
|
||||
xfs_mount_t *mp = src_dp->i_mount;
|
||||
|
@ -2742,6 +2863,18 @@ xfs_rename(
|
|||
goto error_return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle RENAME_EXCHANGE flags
|
||||
*/
|
||||
if (flags & RENAME_EXCHANGE) {
|
||||
error = xfs_cross_rename(tp, src_dp, src_name, src_ip,
|
||||
target_dp, target_name, target_ip,
|
||||
&free_list, &first_block, spaceres);
|
||||
if (error)
|
||||
goto abort_return;
|
||||
goto finish_rename;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up the target.
|
||||
*/
|
||||
|
@ -2881,6 +3014,7 @@ xfs_rename(
|
|||
if (new_parent)
|
||||
xfs_trans_log_inode(tp, target_dp, XFS_ILOG_CORE);
|
||||
|
||||
finish_rename:
|
||||
/*
|
||||
* If this is a synchronous mount, make sure that the
|
||||
* rename transaction goes to disk before returning to
|
||||
|
|
|
@ -338,7 +338,7 @@ int xfs_link(struct xfs_inode *tdp, struct xfs_inode *sip,
|
|||
int xfs_rename(struct xfs_inode *src_dp, struct xfs_name *src_name,
|
||||
struct xfs_inode *src_ip, struct xfs_inode *target_dp,
|
||||
struct xfs_name *target_name,
|
||||
struct xfs_inode *target_ip);
|
||||
struct xfs_inode *target_ip, unsigned int flags);
|
||||
|
||||
void xfs_ilock(xfs_inode_t *, uint);
|
||||
int xfs_ilock_nowait(xfs_inode_t *, uint);
|
||||
|
@ -377,6 +377,15 @@ int xfs_droplink(struct xfs_trans *, struct xfs_inode *);
|
|||
int xfs_bumplink(struct xfs_trans *, struct xfs_inode *);
|
||||
|
||||
/* from xfs_file.c */
|
||||
enum xfs_prealloc_flags {
|
||||
XFS_PREALLOC_SET = (1 << 1),
|
||||
XFS_PREALLOC_CLEAR = (1 << 2),
|
||||
XFS_PREALLOC_SYNC = (1 << 3),
|
||||
XFS_PREALLOC_INVISIBLE = (1 << 4),
|
||||
};
|
||||
|
||||
int xfs_update_prealloc_flags(struct xfs_inode *,
|
||||
enum xfs_prealloc_flags);
|
||||
int xfs_zero_eof(struct xfs_inode *, xfs_off_t, xfs_fsize_t);
|
||||
int xfs_iozero(struct xfs_inode *, loff_t, size_t);
|
||||
|
||||
|
|
|
@ -606,11 +606,8 @@ xfs_ioc_space(
|
|||
unsigned int cmd,
|
||||
xfs_flock64_t *bf)
|
||||
{
|
||||
struct xfs_mount *mp = ip->i_mount;
|
||||
struct xfs_trans *tp;
|
||||
struct iattr iattr;
|
||||
bool setprealloc = false;
|
||||
bool clrprealloc = false;
|
||||
enum xfs_prealloc_flags flags = 0;
|
||||
int error;
|
||||
|
||||
/*
|
||||
|
@ -630,6 +627,11 @@ xfs_ioc_space(
|
|||
if (!S_ISREG(inode->i_mode))
|
||||
return -EINVAL;
|
||||
|
||||
if (filp->f_flags & O_DSYNC)
|
||||
flags |= XFS_PREALLOC_SYNC;
|
||||
if (ioflags & XFS_IO_INVIS)
|
||||
flags |= XFS_PREALLOC_INVISIBLE;
|
||||
|
||||
error = mnt_want_write_file(filp);
|
||||
if (error)
|
||||
return error;
|
||||
|
@ -673,25 +675,23 @@ xfs_ioc_space(
|
|||
}
|
||||
|
||||
if (bf->l_start < 0 ||
|
||||
bf->l_start > mp->m_super->s_maxbytes ||
|
||||
bf->l_start > inode->i_sb->s_maxbytes ||
|
||||
bf->l_start + bf->l_len < 0 ||
|
||||
bf->l_start + bf->l_len >= mp->m_super->s_maxbytes) {
|
||||
bf->l_start + bf->l_len >= inode->i_sb->s_maxbytes) {
|
||||
error = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case XFS_IOC_ZERO_RANGE:
|
||||
flags |= XFS_PREALLOC_SET;
|
||||
error = xfs_zero_file_space(ip, bf->l_start, bf->l_len);
|
||||
if (!error)
|
||||
setprealloc = true;
|
||||
break;
|
||||
case XFS_IOC_RESVSP:
|
||||
case XFS_IOC_RESVSP64:
|
||||
flags |= XFS_PREALLOC_SET;
|
||||
error = xfs_alloc_file_space(ip, bf->l_start, bf->l_len,
|
||||
XFS_BMAPI_PREALLOC);
|
||||
if (!error)
|
||||
setprealloc = true;
|
||||
break;
|
||||
case XFS_IOC_UNRESVSP:
|
||||
case XFS_IOC_UNRESVSP64:
|
||||
|
@ -701,6 +701,7 @@ xfs_ioc_space(
|
|||
case XFS_IOC_ALLOCSP64:
|
||||
case XFS_IOC_FREESP:
|
||||
case XFS_IOC_FREESP64:
|
||||
flags |= XFS_PREALLOC_CLEAR;
|
||||
if (bf->l_start > XFS_ISIZE(ip)) {
|
||||
error = xfs_alloc_file_space(ip, XFS_ISIZE(ip),
|
||||
bf->l_start - XFS_ISIZE(ip), 0);
|
||||
|
@ -712,8 +713,6 @@ xfs_ioc_space(
|
|||
iattr.ia_size = bf->l_start;
|
||||
|
||||
error = xfs_setattr_size(ip, &iattr);
|
||||
if (!error)
|
||||
clrprealloc = true;
|
||||
break;
|
||||
default:
|
||||
ASSERT(0);
|
||||
|
@ -723,32 +722,7 @@ xfs_ioc_space(
|
|||
if (error)
|
||||
goto out_unlock;
|
||||
|
||||
tp = xfs_trans_alloc(mp, XFS_TRANS_WRITEID);
|
||||
error = xfs_trans_reserve(tp, &M_RES(mp)->tr_writeid, 0, 0);
|
||||
if (error) {
|
||||
xfs_trans_cancel(tp, 0);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
xfs_ilock(ip, XFS_ILOCK_EXCL);
|
||||
xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
|
||||
|
||||
if (!(ioflags & XFS_IO_INVIS)) {
|
||||
ip->i_d.di_mode &= ~S_ISUID;
|
||||
if (ip->i_d.di_mode & S_IXGRP)
|
||||
ip->i_d.di_mode &= ~S_ISGID;
|
||||
xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
|
||||
}
|
||||
|
||||
if (setprealloc)
|
||||
ip->i_d.di_flags |= XFS_DIFLAG_PREALLOC;
|
||||
else if (clrprealloc)
|
||||
ip->i_d.di_flags &= ~XFS_DIFLAG_PREALLOC;
|
||||
|
||||
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
|
||||
if (filp->f_flags & O_DSYNC)
|
||||
xfs_trans_set_sync(tp);
|
||||
error = xfs_trans_commit(tp, 0);
|
||||
error = xfs_update_prealloc_flags(ip, flags);
|
||||
|
||||
out_unlock:
|
||||
xfs_iunlock(ip, XFS_IOLOCK_EXCL);
|
||||
|
@ -1013,20 +987,182 @@ xfs_diflags_to_linux(
|
|||
inode->i_flags &= ~S_NOATIME;
|
||||
}
|
||||
|
||||
#define FSX_PROJID 1
|
||||
#define FSX_EXTSIZE 2
|
||||
#define FSX_XFLAGS 4
|
||||
#define FSX_NONBLOCK 8
|
||||
static int
|
||||
xfs_ioctl_setattr_xflags(
|
||||
struct xfs_trans *tp,
|
||||
struct xfs_inode *ip,
|
||||
struct fsxattr *fa)
|
||||
{
|
||||
struct xfs_mount *mp = ip->i_mount;
|
||||
|
||||
/* Can't change realtime flag if any extents are allocated. */
|
||||
if ((ip->i_d.di_nextents || ip->i_delayed_blks) &&
|
||||
XFS_IS_REALTIME_INODE(ip) != (fa->fsx_xflags & XFS_XFLAG_REALTIME))
|
||||
return -EINVAL;
|
||||
|
||||
/* If realtime flag is set then must have realtime device */
|
||||
if (fa->fsx_xflags & XFS_XFLAG_REALTIME) {
|
||||
if (mp->m_sb.sb_rblocks == 0 || mp->m_sb.sb_rextsize == 0 ||
|
||||
(ip->i_d.di_extsize % mp->m_sb.sb_rextsize))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Can't modify an immutable/append-only file unless
|
||||
* we have appropriate permission.
|
||||
*/
|
||||
if (((ip->i_d.di_flags & (XFS_DIFLAG_IMMUTABLE | XFS_DIFLAG_APPEND)) ||
|
||||
(fa->fsx_xflags & (XFS_XFLAG_IMMUTABLE | XFS_XFLAG_APPEND))) &&
|
||||
!capable(CAP_LINUX_IMMUTABLE))
|
||||
return -EPERM;
|
||||
|
||||
xfs_set_diflags(ip, fa->fsx_xflags);
|
||||
xfs_diflags_to_linux(ip);
|
||||
xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG);
|
||||
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
|
||||
XFS_STATS_INC(xs_ig_attrchg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up the transaction structure for the setattr operation, checking that we
|
||||
* have permission to do so. On success, return a clean transaction and the
|
||||
* inode locked exclusively ready for further operation specific checks. On
|
||||
* failure, return an error without modifying or locking the inode.
|
||||
*/
|
||||
static struct xfs_trans *
|
||||
xfs_ioctl_setattr_get_trans(
|
||||
struct xfs_inode *ip)
|
||||
{
|
||||
struct xfs_mount *mp = ip->i_mount;
|
||||
struct xfs_trans *tp;
|
||||
int error;
|
||||
|
||||
if (mp->m_flags & XFS_MOUNT_RDONLY)
|
||||
return ERR_PTR(-EROFS);
|
||||
if (XFS_FORCED_SHUTDOWN(mp))
|
||||
return ERR_PTR(-EIO);
|
||||
|
||||
tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_NOT_SIZE);
|
||||
error = xfs_trans_reserve(tp, &M_RES(mp)->tr_ichange, 0, 0);
|
||||
if (error)
|
||||
goto out_cancel;
|
||||
|
||||
xfs_ilock(ip, XFS_ILOCK_EXCL);
|
||||
xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
|
||||
|
||||
/*
|
||||
* CAP_FOWNER overrides the following restrictions:
|
||||
*
|
||||
* The user ID of the calling process must be equal to the file owner
|
||||
* ID, except in cases where the CAP_FSETID capability is applicable.
|
||||
*/
|
||||
if (!inode_owner_or_capable(VFS_I(ip))) {
|
||||
error = -EPERM;
|
||||
goto out_cancel;
|
||||
}
|
||||
|
||||
if (mp->m_flags & XFS_MOUNT_WSYNC)
|
||||
xfs_trans_set_sync(tp);
|
||||
|
||||
return tp;
|
||||
|
||||
out_cancel:
|
||||
xfs_trans_cancel(tp, 0);
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
|
||||
/*
|
||||
* extent size hint validation is somewhat cumbersome. Rules are:
|
||||
*
|
||||
* 1. extent size hint is only valid for directories and regular files
|
||||
* 2. XFS_XFLAG_EXTSIZE is only valid for regular files
|
||||
* 3. XFS_XFLAG_EXTSZINHERIT is only valid for directories.
|
||||
* 4. can only be changed on regular files if no extents are allocated
|
||||
* 5. can be changed on directories at any time
|
||||
* 6. extsize hint of 0 turns off hints, clears inode flags.
|
||||
* 7. Extent size must be a multiple of the appropriate block size.
|
||||
* 8. for non-realtime files, the extent size hint must be limited
|
||||
* to half the AG size to avoid alignment extending the extent beyond the
|
||||
* limits of the AG.
|
||||
*/
|
||||
static int
|
||||
xfs_ioctl_setattr_check_extsize(
|
||||
struct xfs_inode *ip,
|
||||
struct fsxattr *fa)
|
||||
{
|
||||
struct xfs_mount *mp = ip->i_mount;
|
||||
|
||||
if ((fa->fsx_xflags & XFS_XFLAG_EXTSIZE) && !S_ISREG(ip->i_d.di_mode))
|
||||
return -EINVAL;
|
||||
|
||||
if ((fa->fsx_xflags & XFS_XFLAG_EXTSZINHERIT) &&
|
||||
!S_ISDIR(ip->i_d.di_mode))
|
||||
return -EINVAL;
|
||||
|
||||
if (S_ISREG(ip->i_d.di_mode) && ip->i_d.di_nextents &&
|
||||
((ip->i_d.di_extsize << mp->m_sb.sb_blocklog) != fa->fsx_extsize))
|
||||
return -EINVAL;
|
||||
|
||||
if (fa->fsx_extsize != 0) {
|
||||
xfs_extlen_t size;
|
||||
xfs_fsblock_t extsize_fsb;
|
||||
|
||||
extsize_fsb = XFS_B_TO_FSB(mp, fa->fsx_extsize);
|
||||
if (extsize_fsb > MAXEXTLEN)
|
||||
return -EINVAL;
|
||||
|
||||
if (XFS_IS_REALTIME_INODE(ip) ||
|
||||
(fa->fsx_xflags & XFS_XFLAG_REALTIME)) {
|
||||
size = mp->m_sb.sb_rextsize << mp->m_sb.sb_blocklog;
|
||||
} else {
|
||||
size = mp->m_sb.sb_blocksize;
|
||||
if (extsize_fsb > mp->m_sb.sb_agblocks / 2)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (fa->fsx_extsize % size)
|
||||
return -EINVAL;
|
||||
} else
|
||||
fa->fsx_xflags &= ~(XFS_XFLAG_EXTSIZE | XFS_XFLAG_EXTSZINHERIT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
xfs_ioctl_setattr_check_projid(
|
||||
struct xfs_inode *ip,
|
||||
struct fsxattr *fa)
|
||||
{
|
||||
/* Disallow 32bit project ids if projid32bit feature is not enabled. */
|
||||
if (fa->fsx_projid > (__uint16_t)-1 &&
|
||||
!xfs_sb_version_hasprojid32bit(&ip->i_mount->m_sb))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Project Quota ID state is only allowed to change from within the init
|
||||
* namespace. Enforce that restriction only if we are trying to change
|
||||
* the quota ID state. Everything else is allowed in user namespaces.
|
||||
*/
|
||||
if (current_user_ns() == &init_user_ns)
|
||||
return 0;
|
||||
|
||||
if (xfs_get_projid(ip) != fa->fsx_projid)
|
||||
return -EINVAL;
|
||||
if ((fa->fsx_xflags & XFS_XFLAG_PROJINHERIT) !=
|
||||
(ip->i_d.di_flags & XFS_DIFLAG_PROJINHERIT))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
STATIC int
|
||||
xfs_ioctl_setattr(
|
||||
xfs_inode_t *ip,
|
||||
struct fsxattr *fa,
|
||||
int mask)
|
||||
struct fsxattr *fa)
|
||||
{
|
||||
struct xfs_mount *mp = ip->i_mount;
|
||||
struct xfs_trans *tp;
|
||||
unsigned int lock_flags = 0;
|
||||
struct xfs_dquot *udqp = NULL;
|
||||
struct xfs_dquot *pdqp = NULL;
|
||||
struct xfs_dquot *olddquot = NULL;
|
||||
|
@ -1034,17 +1170,9 @@ xfs_ioctl_setattr(
|
|||
|
||||
trace_xfs_ioctl_setattr(ip);
|
||||
|
||||
if (mp->m_flags & XFS_MOUNT_RDONLY)
|
||||
return -EROFS;
|
||||
if (XFS_FORCED_SHUTDOWN(mp))
|
||||
return -EIO;
|
||||
|
||||
/*
|
||||
* Disallow 32bit project ids when projid32bit feature is not enabled.
|
||||
*/
|
||||
if ((mask & FSX_PROJID) && (fa->fsx_projid > (__uint16_t)-1) &&
|
||||
!xfs_sb_version_hasprojid32bit(&ip->i_mount->m_sb))
|
||||
return -EINVAL;
|
||||
code = xfs_ioctl_setattr_check_projid(ip, fa);
|
||||
if (code)
|
||||
return code;
|
||||
|
||||
/*
|
||||
* If disk quotas is on, we make sure that the dquots do exist on disk,
|
||||
|
@ -1054,7 +1182,7 @@ xfs_ioctl_setattr(
|
|||
* If the IDs do change before we take the ilock, we're covered
|
||||
* because the i_*dquot fields will get updated anyway.
|
||||
*/
|
||||
if (XFS_IS_QUOTA_ON(mp) && (mask & FSX_PROJID)) {
|
||||
if (XFS_IS_QUOTA_ON(mp)) {
|
||||
code = xfs_qm_vop_dqalloc(ip, ip->i_d.di_uid,
|
||||
ip->i_d.di_gid, fa->fsx_projid,
|
||||
XFS_QMOPT_PQUOTA, &udqp, NULL, &pdqp);
|
||||
|
@ -1062,175 +1190,49 @@ xfs_ioctl_setattr(
|
|||
return code;
|
||||
}
|
||||
|
||||
/*
|
||||
* For the other attributes, we acquire the inode lock and
|
||||
* first do an error checking pass.
|
||||
*/
|
||||
tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_NOT_SIZE);
|
||||
code = xfs_trans_reserve(tp, &M_RES(mp)->tr_ichange, 0, 0);
|
||||
tp = xfs_ioctl_setattr_get_trans(ip);
|
||||
if (IS_ERR(tp)) {
|
||||
code = PTR_ERR(tp);
|
||||
goto error_free_dquots;
|
||||
}
|
||||
|
||||
|
||||
if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp) &&
|
||||
xfs_get_projid(ip) != fa->fsx_projid) {
|
||||
code = xfs_qm_vop_chown_reserve(tp, ip, udqp, NULL, pdqp,
|
||||
capable(CAP_FOWNER) ? XFS_QMOPT_FORCE_RES : 0);
|
||||
if (code) /* out of quota */
|
||||
goto error_trans_cancel;
|
||||
}
|
||||
|
||||
code = xfs_ioctl_setattr_check_extsize(ip, fa);
|
||||
if (code)
|
||||
goto error_return;
|
||||
goto error_trans_cancel;
|
||||
|
||||
lock_flags = XFS_ILOCK_EXCL;
|
||||
xfs_ilock(ip, lock_flags);
|
||||
code = xfs_ioctl_setattr_xflags(tp, ip, fa);
|
||||
if (code)
|
||||
goto error_trans_cancel;
|
||||
|
||||
/*
|
||||
* CAP_FOWNER overrides the following restrictions:
|
||||
* Change file ownership. Must be the owner or privileged. CAP_FSETID
|
||||
* overrides the following restrictions:
|
||||
*
|
||||
* The user ID of the calling process must be equal
|
||||
* to the file owner ID, except in cases where the
|
||||
* CAP_FSETID capability is applicable.
|
||||
* The set-user-ID and set-group-ID bits of a file will be cleared upon
|
||||
* successful return from chown()
|
||||
*/
|
||||
if (!inode_owner_or_capable(VFS_I(ip))) {
|
||||
code = -EPERM;
|
||||
goto error_return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do a quota reservation only if projid is actually going to change.
|
||||
* Only allow changing of projid from init_user_ns since it is a
|
||||
* non user namespace aware identifier.
|
||||
*/
|
||||
if (mask & FSX_PROJID) {
|
||||
if (current_user_ns() != &init_user_ns) {
|
||||
code = -EINVAL;
|
||||
goto error_return;
|
||||
if ((ip->i_d.di_mode & (S_ISUID|S_ISGID)) &&
|
||||
!capable_wrt_inode_uidgid(VFS_I(ip), CAP_FSETID))
|
||||
ip->i_d.di_mode &= ~(S_ISUID|S_ISGID);
|
||||
|
||||
/* Change the ownerships and register project quota modifications */
|
||||
if (xfs_get_projid(ip) != fa->fsx_projid) {
|
||||
if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp)) {
|
||||
olddquot = xfs_qm_vop_chown(tp, ip,
|
||||
&ip->i_pdquot, pdqp);
|
||||
}
|
||||
|
||||
if (XFS_IS_QUOTA_RUNNING(mp) &&
|
||||
XFS_IS_PQUOTA_ON(mp) &&
|
||||
xfs_get_projid(ip) != fa->fsx_projid) {
|
||||
ASSERT(tp);
|
||||
code = xfs_qm_vop_chown_reserve(tp, ip, udqp, NULL,
|
||||
pdqp, capable(CAP_FOWNER) ?
|
||||
XFS_QMOPT_FORCE_RES : 0);
|
||||
if (code) /* out of quota */
|
||||
goto error_return;
|
||||
}
|
||||
}
|
||||
|
||||
if (mask & FSX_EXTSIZE) {
|
||||
/*
|
||||
* Can't change extent size if any extents are allocated.
|
||||
*/
|
||||
if (ip->i_d.di_nextents &&
|
||||
((ip->i_d.di_extsize << mp->m_sb.sb_blocklog) !=
|
||||
fa->fsx_extsize)) {
|
||||
code = -EINVAL; /* EFBIG? */
|
||||
goto error_return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extent size must be a multiple of the appropriate block
|
||||
* size, if set at all. It must also be smaller than the
|
||||
* maximum extent size supported by the filesystem.
|
||||
*
|
||||
* Also, for non-realtime files, limit the extent size hint to
|
||||
* half the size of the AGs in the filesystem so alignment
|
||||
* doesn't result in extents larger than an AG.
|
||||
*/
|
||||
if (fa->fsx_extsize != 0) {
|
||||
xfs_extlen_t size;
|
||||
xfs_fsblock_t extsize_fsb;
|
||||
|
||||
extsize_fsb = XFS_B_TO_FSB(mp, fa->fsx_extsize);
|
||||
if (extsize_fsb > MAXEXTLEN) {
|
||||
code = -EINVAL;
|
||||
goto error_return;
|
||||
}
|
||||
|
||||
if (XFS_IS_REALTIME_INODE(ip) ||
|
||||
((mask & FSX_XFLAGS) &&
|
||||
(fa->fsx_xflags & XFS_XFLAG_REALTIME))) {
|
||||
size = mp->m_sb.sb_rextsize <<
|
||||
mp->m_sb.sb_blocklog;
|
||||
} else {
|
||||
size = mp->m_sb.sb_blocksize;
|
||||
if (extsize_fsb > mp->m_sb.sb_agblocks / 2) {
|
||||
code = -EINVAL;
|
||||
goto error_return;
|
||||
}
|
||||
}
|
||||
|
||||
if (fa->fsx_extsize % size) {
|
||||
code = -EINVAL;
|
||||
goto error_return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (mask & FSX_XFLAGS) {
|
||||
/*
|
||||
* Can't change realtime flag if any extents are allocated.
|
||||
*/
|
||||
if ((ip->i_d.di_nextents || ip->i_delayed_blks) &&
|
||||
(XFS_IS_REALTIME_INODE(ip)) !=
|
||||
(fa->fsx_xflags & XFS_XFLAG_REALTIME)) {
|
||||
code = -EINVAL; /* EFBIG? */
|
||||
goto error_return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If realtime flag is set then must have realtime data.
|
||||
*/
|
||||
if ((fa->fsx_xflags & XFS_XFLAG_REALTIME)) {
|
||||
if ((mp->m_sb.sb_rblocks == 0) ||
|
||||
(mp->m_sb.sb_rextsize == 0) ||
|
||||
(ip->i_d.di_extsize % mp->m_sb.sb_rextsize)) {
|
||||
code = -EINVAL;
|
||||
goto error_return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Can't modify an immutable/append-only file unless
|
||||
* we have appropriate permission.
|
||||
*/
|
||||
if ((ip->i_d.di_flags &
|
||||
(XFS_DIFLAG_IMMUTABLE|XFS_DIFLAG_APPEND) ||
|
||||
(fa->fsx_xflags &
|
||||
(XFS_XFLAG_IMMUTABLE | XFS_XFLAG_APPEND))) &&
|
||||
!capable(CAP_LINUX_IMMUTABLE)) {
|
||||
code = -EPERM;
|
||||
goto error_return;
|
||||
}
|
||||
}
|
||||
|
||||
xfs_trans_ijoin(tp, ip, 0);
|
||||
|
||||
/*
|
||||
* Change file ownership. Must be the owner or privileged.
|
||||
*/
|
||||
if (mask & FSX_PROJID) {
|
||||
/*
|
||||
* CAP_FSETID overrides the following restrictions:
|
||||
*
|
||||
* The set-user-ID and set-group-ID bits of a file will be
|
||||
* cleared upon successful return from chown()
|
||||
*/
|
||||
if ((ip->i_d.di_mode & (S_ISUID|S_ISGID)) &&
|
||||
!capable_wrt_inode_uidgid(VFS_I(ip), CAP_FSETID))
|
||||
ip->i_d.di_mode &= ~(S_ISUID|S_ISGID);
|
||||
|
||||
/*
|
||||
* Change the ownerships and register quota modifications
|
||||
* in the transaction.
|
||||
*/
|
||||
if (xfs_get_projid(ip) != fa->fsx_projid) {
|
||||
if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp)) {
|
||||
olddquot = xfs_qm_vop_chown(tp, ip,
|
||||
&ip->i_pdquot, pdqp);
|
||||
}
|
||||
ASSERT(ip->i_d.di_version > 1);
|
||||
xfs_set_projid(ip, fa->fsx_projid);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (mask & FSX_XFLAGS) {
|
||||
xfs_set_diflags(ip, fa->fsx_xflags);
|
||||
xfs_diflags_to_linux(ip);
|
||||
ASSERT(ip->i_d.di_version > 1);
|
||||
xfs_set_projid(ip, fa->fsx_projid);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1238,34 +1240,12 @@ xfs_ioctl_setattr(
|
|||
* extent size hint should be set on the inode. If no extent size flags
|
||||
* are set on the inode then unconditionally clear the extent size hint.
|
||||
*/
|
||||
if (mask & FSX_EXTSIZE) {
|
||||
int extsize = 0;
|
||||
if (ip->i_d.di_flags & (XFS_DIFLAG_EXTSIZE | XFS_DIFLAG_EXTSZINHERIT))
|
||||
ip->i_d.di_extsize = fa->fsx_extsize >> mp->m_sb.sb_blocklog;
|
||||
else
|
||||
ip->i_d.di_extsize = 0;
|
||||
|
||||
if (ip->i_d.di_flags &
|
||||
(XFS_DIFLAG_EXTSIZE | XFS_DIFLAG_EXTSZINHERIT))
|
||||
extsize = fa->fsx_extsize >> mp->m_sb.sb_blocklog;
|
||||
ip->i_d.di_extsize = extsize;
|
||||
}
|
||||
|
||||
xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG);
|
||||
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
|
||||
|
||||
XFS_STATS_INC(xs_ig_attrchg);
|
||||
|
||||
/*
|
||||
* If this is a synchronous mount, make sure that the
|
||||
* transaction goes to disk before returning to the user.
|
||||
* This is slightly sub-optimal in that truncates require
|
||||
* two sync transactions instead of one for wsync filesystems.
|
||||
* One for the truncate and one for the timestamps since we
|
||||
* don't want to change the timestamps unless we're sure the
|
||||
* truncate worked. Truncates are less than 1% of the laddis
|
||||
* mix so this probably isn't worth the trouble to optimize.
|
||||
*/
|
||||
if (mp->m_flags & XFS_MOUNT_WSYNC)
|
||||
xfs_trans_set_sync(tp);
|
||||
code = xfs_trans_commit(tp, 0);
|
||||
xfs_iunlock(ip, lock_flags);
|
||||
|
||||
/*
|
||||
* Release any dquot(s) the inode had kept before chown.
|
||||
|
@ -1276,12 +1256,11 @@ xfs_ioctl_setattr(
|
|||
|
||||
return code;
|
||||
|
||||
error_return:
|
||||
error_trans_cancel:
|
||||
xfs_trans_cancel(tp, 0);
|
||||
error_free_dquots:
|
||||
xfs_qm_dqrele(udqp);
|
||||
xfs_qm_dqrele(pdqp);
|
||||
xfs_trans_cancel(tp, 0);
|
||||
if (lock_flags)
|
||||
xfs_iunlock(ip, lock_flags);
|
||||
return code;
|
||||
}
|
||||
|
||||
|
@ -1292,20 +1271,15 @@ xfs_ioc_fssetxattr(
|
|||
void __user *arg)
|
||||
{
|
||||
struct fsxattr fa;
|
||||
unsigned int mask;
|
||||
int error;
|
||||
|
||||
if (copy_from_user(&fa, arg, sizeof(fa)))
|
||||
return -EFAULT;
|
||||
|
||||
mask = FSX_XFLAGS | FSX_EXTSIZE | FSX_PROJID;
|
||||
if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
|
||||
mask |= FSX_NONBLOCK;
|
||||
|
||||
error = mnt_want_write_file(filp);
|
||||
if (error)
|
||||
return error;
|
||||
error = xfs_ioctl_setattr(ip, &fa, mask);
|
||||
error = xfs_ioctl_setattr(ip, &fa);
|
||||
mnt_drop_write_file(filp);
|
||||
return error;
|
||||
}
|
||||
|
@ -1325,14 +1299,14 @@ xfs_ioc_getxflags(
|
|||
|
||||
STATIC int
|
||||
xfs_ioc_setxflags(
|
||||
xfs_inode_t *ip,
|
||||
struct xfs_inode *ip,
|
||||
struct file *filp,
|
||||
void __user *arg)
|
||||
{
|
||||
struct xfs_trans *tp;
|
||||
struct fsxattr fa;
|
||||
unsigned int flags;
|
||||
unsigned int mask;
|
||||
int error;
|
||||
int error;
|
||||
|
||||
if (copy_from_user(&flags, arg, sizeof(flags)))
|
||||
return -EFAULT;
|
||||
|
@ -1342,15 +1316,26 @@ xfs_ioc_setxflags(
|
|||
FS_SYNC_FL))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
mask = FSX_XFLAGS;
|
||||
if (filp->f_flags & (O_NDELAY|O_NONBLOCK))
|
||||
mask |= FSX_NONBLOCK;
|
||||
fa.fsx_xflags = xfs_merge_ioc_xflags(flags, xfs_ip2xflags(ip));
|
||||
|
||||
error = mnt_want_write_file(filp);
|
||||
if (error)
|
||||
return error;
|
||||
error = xfs_ioctl_setattr(ip, &fa, mask);
|
||||
|
||||
tp = xfs_ioctl_setattr_get_trans(ip);
|
||||
if (IS_ERR(tp)) {
|
||||
error = PTR_ERR(tp);
|
||||
goto out_drop_write;
|
||||
}
|
||||
|
||||
error = xfs_ioctl_setattr_xflags(tp, ip, &fa);
|
||||
if (error) {
|
||||
xfs_trans_cancel(tp, 0);
|
||||
goto out_drop_write;
|
||||
}
|
||||
|
||||
error = xfs_trans_commit(tp, 0);
|
||||
out_drop_write:
|
||||
mnt_drop_write_file(filp);
|
||||
return error;
|
||||
}
|
||||
|
|
|
@ -423,7 +423,7 @@ xfs_compat_attrmulti_by_handle(
|
|||
|
||||
ops = memdup_user(compat_ptr(am_hreq.ops), size);
|
||||
if (IS_ERR(ops)) {
|
||||
error = -PTR_ERR(ops);
|
||||
error = PTR_ERR(ops);
|
||||
goto out_dput;
|
||||
}
|
||||
|
||||
|
|
|
@ -802,7 +802,7 @@ int
|
|||
xfs_iomap_write_unwritten(
|
||||
xfs_inode_t *ip,
|
||||
xfs_off_t offset,
|
||||
size_t count)
|
||||
xfs_off_t count)
|
||||
{
|
||||
xfs_mount_t *mp = ip->i_mount;
|
||||
xfs_fileoff_t offset_fsb;
|
||||
|
|
|
@ -27,6 +27,6 @@ int xfs_iomap_write_delay(struct xfs_inode *, xfs_off_t, size_t,
|
|||
struct xfs_bmbt_irec *);
|
||||
int xfs_iomap_write_allocate(struct xfs_inode *, xfs_off_t,
|
||||
struct xfs_bmbt_irec *);
|
||||
int xfs_iomap_write_unwritten(struct xfs_inode *, xfs_off_t, size_t);
|
||||
int xfs_iomap_write_unwritten(struct xfs_inode *, xfs_off_t, xfs_off_t);
|
||||
|
||||
#endif /* __XFS_IOMAP_H__*/
|
||||
|
|
|
@ -380,18 +380,27 @@ xfs_vn_rename(
|
|||
struct inode *odir,
|
||||
struct dentry *odentry,
|
||||
struct inode *ndir,
|
||||
struct dentry *ndentry)
|
||||
struct dentry *ndentry,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct inode *new_inode = ndentry->d_inode;
|
||||
int omode = 0;
|
||||
struct xfs_name oname;
|
||||
struct xfs_name nname;
|
||||
|
||||
xfs_dentry_to_name(&oname, odentry, 0);
|
||||
if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE))
|
||||
return -EINVAL;
|
||||
|
||||
/* if we are exchanging files, we need to set i_mode of both files */
|
||||
if (flags & RENAME_EXCHANGE)
|
||||
omode = ndentry->d_inode->i_mode;
|
||||
|
||||
xfs_dentry_to_name(&oname, odentry, omode);
|
||||
xfs_dentry_to_name(&nname, ndentry, odentry->d_inode->i_mode);
|
||||
|
||||
return xfs_rename(XFS_I(odir), &oname, XFS_I(odentry->d_inode),
|
||||
XFS_I(ndir), &nname, new_inode ?
|
||||
XFS_I(new_inode) : NULL);
|
||||
XFS_I(ndir), &nname,
|
||||
new_inode ? XFS_I(new_inode) : NULL, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1144,7 +1153,7 @@ static const struct inode_operations xfs_dir_inode_operations = {
|
|||
*/
|
||||
.rmdir = xfs_vn_unlink,
|
||||
.mknod = xfs_vn_mknod,
|
||||
.rename = xfs_vn_rename,
|
||||
.rename2 = xfs_vn_rename,
|
||||
.get_acl = xfs_get_acl,
|
||||
.set_acl = xfs_set_acl,
|
||||
.getattr = xfs_vn_getattr,
|
||||
|
@ -1172,7 +1181,7 @@ static const struct inode_operations xfs_dir_ci_inode_operations = {
|
|||
*/
|
||||
.rmdir = xfs_vn_unlink,
|
||||
.mknod = xfs_vn_mknod,
|
||||
.rename = xfs_vn_rename,
|
||||
.rename2 = xfs_vn_rename,
|
||||
.get_acl = xfs_get_acl,
|
||||
.set_acl = xfs_set_acl,
|
||||
.getattr = xfs_vn_getattr,
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "xfs_fsops.h"
|
||||
#include "xfs_cksum.h"
|
||||
#include "xfs_sysfs.h"
|
||||
#include "xfs_sb.h"
|
||||
|
||||
kmem_zone_t *xfs_log_ticket_zone;
|
||||
|
||||
|
@ -1290,9 +1291,20 @@ xfs_log_worker(
|
|||
struct xfs_mount *mp = log->l_mp;
|
||||
|
||||
/* dgc: errors ignored - not fatal and nowhere to report them */
|
||||
if (xfs_log_need_covered(mp))
|
||||
xfs_fs_log_dummy(mp);
|
||||
else
|
||||
if (xfs_log_need_covered(mp)) {
|
||||
/*
|
||||
* Dump a transaction into the log that contains no real change.
|
||||
* This is needed to stamp the current tail LSN into the log
|
||||
* during the covering operation.
|
||||
*
|
||||
* We cannot use an inode here for this - that will push dirty
|
||||
* state back up into the VFS and then periodic inode flushing
|
||||
* will prevent log covering from making progress. Hence we
|
||||
* synchronously log the superblock instead to ensure the
|
||||
* superblock is immediately unpinned and can be written back.
|
||||
*/
|
||||
xfs_sync_sb(mp, true);
|
||||
} else
|
||||
xfs_log_force(mp, 0);
|
||||
|
||||
/* start pushing all the metadata that is currently dirty */
|
||||
|
@ -1395,6 +1407,8 @@ xlog_alloc_log(
|
|||
ASSERT(xfs_buf_islocked(bp));
|
||||
xfs_buf_unlock(bp);
|
||||
|
||||
/* use high priority wq for log I/O completion */
|
||||
bp->b_ioend_wq = mp->m_log_workqueue;
|
||||
bp->b_iodone = xlog_iodone;
|
||||
log->l_xbuf = bp;
|
||||
|
||||
|
@ -1427,6 +1441,8 @@ xlog_alloc_log(
|
|||
ASSERT(xfs_buf_islocked(bp));
|
||||
xfs_buf_unlock(bp);
|
||||
|
||||
/* use high priority wq for log I/O completion */
|
||||
bp->b_ioend_wq = mp->m_log_workqueue;
|
||||
bp->b_iodone = xlog_iodone;
|
||||
iclog->ic_bp = bp;
|
||||
iclog->ic_data = bp->b_addr;
|
||||
|
@ -1806,8 +1822,6 @@ xlog_sync(
|
|||
XFS_BUF_ZEROFLAGS(bp);
|
||||
XFS_BUF_ASYNC(bp);
|
||||
bp->b_flags |= XBF_SYNCIO;
|
||||
/* use high priority completion wq */
|
||||
bp->b_ioend_wq = log->l_mp->m_log_workqueue;
|
||||
|
||||
if (log->l_mp->m_flags & XFS_MOUNT_BARRIER) {
|
||||
bp->b_flags |= XBF_FUA;
|
||||
|
@ -1856,8 +1870,6 @@ xlog_sync(
|
|||
bp->b_flags |= XBF_SYNCIO;
|
||||
if (log->l_mp->m_flags & XFS_MOUNT_BARRIER)
|
||||
bp->b_flags |= XBF_FUA;
|
||||
/* use high priority completion wq */
|
||||
bp->b_ioend_wq = log->l_mp->m_log_workqueue;
|
||||
|
||||
ASSERT(XFS_BUF_ADDR(bp) <= log->l_logBBsize-1);
|
||||
ASSERT(XFS_BUF_ADDR(bp) + BTOBB(count) <= log->l_logBBsize);
|
||||
|
@ -2027,7 +2039,7 @@ xlog_print_tic_res(
|
|||
" total reg = %u bytes (o/flow = %u bytes)\n"
|
||||
" ophdrs = %u (ophdr space = %u bytes)\n"
|
||||
" ophdr + reg = %u bytes\n"
|
||||
" num regions = %u\n",
|
||||
" num regions = %u",
|
||||
((ticket->t_trans_type <= 0 ||
|
||||
ticket->t_trans_type > XFS_TRANS_TYPE_MAX) ?
|
||||
"bad-trans-type" : trans_type_str[ticket->t_trans_type-1]),
|
||||
|
|
|
@ -408,11 +408,11 @@ xfs_update_alignment(xfs_mount_t *mp)
|
|||
if (xfs_sb_version_hasdalign(sbp)) {
|
||||
if (sbp->sb_unit != mp->m_dalign) {
|
||||
sbp->sb_unit = mp->m_dalign;
|
||||
mp->m_update_flags |= XFS_SB_UNIT;
|
||||
mp->m_update_sb = true;
|
||||
}
|
||||
if (sbp->sb_width != mp->m_swidth) {
|
||||
sbp->sb_width = mp->m_swidth;
|
||||
mp->m_update_flags |= XFS_SB_WIDTH;
|
||||
mp->m_update_sb = true;
|
||||
}
|
||||
} else {
|
||||
xfs_warn(mp,
|
||||
|
@ -583,38 +583,19 @@ int
|
|||
xfs_mount_reset_sbqflags(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
int error;
|
||||
struct xfs_trans *tp;
|
||||
|
||||
mp->m_qflags = 0;
|
||||
|
||||
/*
|
||||
* It is OK to look at sb_qflags here in mount path,
|
||||
* without m_sb_lock.
|
||||
*/
|
||||
/* It is OK to look at sb_qflags in the mount path without m_sb_lock. */
|
||||
if (mp->m_sb.sb_qflags == 0)
|
||||
return 0;
|
||||
spin_lock(&mp->m_sb_lock);
|
||||
mp->m_sb.sb_qflags = 0;
|
||||
spin_unlock(&mp->m_sb_lock);
|
||||
|
||||
/*
|
||||
* If the fs is readonly, let the incore superblock run
|
||||
* with quotas off but don't flush the update out to disk
|
||||
*/
|
||||
if (mp->m_flags & XFS_MOUNT_RDONLY)
|
||||
if (!xfs_fs_writable(mp, SB_FREEZE_WRITE))
|
||||
return 0;
|
||||
|
||||
tp = xfs_trans_alloc(mp, XFS_TRANS_QM_SBCHANGE);
|
||||
error = xfs_trans_reserve(tp, &M_RES(mp)->tr_qm_sbchange, 0, 0);
|
||||
if (error) {
|
||||
xfs_trans_cancel(tp, 0);
|
||||
xfs_alert(mp, "%s: Superblock update failed!", __func__);
|
||||
return error;
|
||||
}
|
||||
|
||||
xfs_mod_sb(tp, XFS_SB_QFLAGS);
|
||||
return xfs_trans_commit(tp, 0);
|
||||
return xfs_sync_sb(mp, false);
|
||||
}
|
||||
|
||||
__uint64_t
|
||||
|
@ -659,26 +640,25 @@ xfs_mountfs(
|
|||
xfs_sb_mount_common(mp, sbp);
|
||||
|
||||
/*
|
||||
* Check for a mismatched features2 values. Older kernels
|
||||
* read & wrote into the wrong sb offset for sb_features2
|
||||
* on some platforms due to xfs_sb_t not being 64bit size aligned
|
||||
* when sb_features2 was added, which made older superblock
|
||||
* reading/writing routines swap it as a 64-bit value.
|
||||
* Check for a mismatched features2 values. Older kernels read & wrote
|
||||
* into the wrong sb offset for sb_features2 on some platforms due to
|
||||
* xfs_sb_t not being 64bit size aligned when sb_features2 was added,
|
||||
* which made older superblock reading/writing routines swap it as a
|
||||
* 64-bit value.
|
||||
*
|
||||
* For backwards compatibility, we make both slots equal.
|
||||
*
|
||||
* If we detect a mismatched field, we OR the set bits into the
|
||||
* existing features2 field in case it has already been modified; we
|
||||
* don't want to lose any features. We then update the bad location
|
||||
* with the ORed value so that older kernels will see any features2
|
||||
* flags, and mark the two fields as needing updates once the
|
||||
* transaction subsystem is online.
|
||||
* If we detect a mismatched field, we OR the set bits into the existing
|
||||
* features2 field in case it has already been modified; we don't want
|
||||
* to lose any features. We then update the bad location with the ORed
|
||||
* value so that older kernels will see any features2 flags. The
|
||||
* superblock writeback code ensures the new sb_features2 is copied to
|
||||
* sb_bad_features2 before it is logged or written to disk.
|
||||
*/
|
||||
if (xfs_sb_has_mismatched_features2(sbp)) {
|
||||
xfs_warn(mp, "correcting sb_features alignment problem");
|
||||
sbp->sb_features2 |= sbp->sb_bad_features2;
|
||||
sbp->sb_bad_features2 = sbp->sb_features2;
|
||||
mp->m_update_flags |= XFS_SB_FEATURES2 | XFS_SB_BAD_FEATURES2;
|
||||
mp->m_update_sb = true;
|
||||
|
||||
/*
|
||||
* Re-check for ATTR2 in case it was found in bad_features2
|
||||
|
@ -692,17 +672,17 @@ xfs_mountfs(
|
|||
if (xfs_sb_version_hasattr2(&mp->m_sb) &&
|
||||
(mp->m_flags & XFS_MOUNT_NOATTR2)) {
|
||||
xfs_sb_version_removeattr2(&mp->m_sb);
|
||||
mp->m_update_flags |= XFS_SB_FEATURES2;
|
||||
mp->m_update_sb = true;
|
||||
|
||||
/* update sb_versionnum for the clearing of the morebits */
|
||||
if (!sbp->sb_features2)
|
||||
mp->m_update_flags |= XFS_SB_VERSIONNUM;
|
||||
mp->m_update_sb = true;
|
||||
}
|
||||
|
||||
/* always use v2 inodes by default now */
|
||||
if (!(mp->m_sb.sb_versionnum & XFS_SB_VERSION_NLINKBIT)) {
|
||||
mp->m_sb.sb_versionnum |= XFS_SB_VERSION_NLINKBIT;
|
||||
mp->m_update_flags |= XFS_SB_VERSIONNUM;
|
||||
mp->m_update_sb = true;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -895,8 +875,8 @@ xfs_mountfs(
|
|||
* the next remount into writeable mode. Otherwise we would never
|
||||
* perform the update e.g. for the root filesystem.
|
||||
*/
|
||||
if (mp->m_update_flags && !(mp->m_flags & XFS_MOUNT_RDONLY)) {
|
||||
error = xfs_mount_log_sb(mp, mp->m_update_flags);
|
||||
if (mp->m_update_sb && !(mp->m_flags & XFS_MOUNT_RDONLY)) {
|
||||
error = xfs_sync_sb(mp, false);
|
||||
if (error) {
|
||||
xfs_warn(mp, "failed to write sb changes");
|
||||
goto out_rtunmount;
|
||||
|
@ -1103,9 +1083,6 @@ xfs_fs_writable(
|
|||
int
|
||||
xfs_log_sbcount(xfs_mount_t *mp)
|
||||
{
|
||||
xfs_trans_t *tp;
|
||||
int error;
|
||||
|
||||
/* allow this to proceed during the freeze sequence... */
|
||||
if (!xfs_fs_writable(mp, SB_FREEZE_COMPLETE))
|
||||
return 0;
|
||||
|
@ -1119,17 +1096,7 @@ xfs_log_sbcount(xfs_mount_t *mp)
|
|||
if (!xfs_sb_version_haslazysbcount(&mp->m_sb))
|
||||
return 0;
|
||||
|
||||
tp = _xfs_trans_alloc(mp, XFS_TRANS_SB_COUNT, KM_SLEEP);
|
||||
error = xfs_trans_reserve(tp, &M_RES(mp)->tr_sb, 0, 0);
|
||||
if (error) {
|
||||
xfs_trans_cancel(tp, 0);
|
||||
return error;
|
||||
}
|
||||
|
||||
xfs_mod_sb(tp, XFS_SB_IFREE | XFS_SB_ICOUNT | XFS_SB_FDBLOCKS);
|
||||
xfs_trans_set_sync(tp);
|
||||
error = xfs_trans_commit(tp, 0);
|
||||
return error;
|
||||
return xfs_sync_sb(mp, true);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1422,34 +1389,6 @@ xfs_freesb(
|
|||
xfs_buf_relse(bp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Used to log changes to the superblock unit and width fields which could
|
||||
* be altered by the mount options, as well as any potential sb_features2
|
||||
* fixup. Only the first superblock is updated.
|
||||
*/
|
||||
int
|
||||
xfs_mount_log_sb(
|
||||
xfs_mount_t *mp,
|
||||
__int64_t fields)
|
||||
{
|
||||
xfs_trans_t *tp;
|
||||
int error;
|
||||
|
||||
ASSERT(fields & (XFS_SB_UNIT | XFS_SB_WIDTH | XFS_SB_UUID |
|
||||
XFS_SB_FEATURES2 | XFS_SB_BAD_FEATURES2 |
|
||||
XFS_SB_VERSIONNUM));
|
||||
|
||||
tp = xfs_trans_alloc(mp, XFS_TRANS_SB_UNIT);
|
||||
error = xfs_trans_reserve(tp, &M_RES(mp)->tr_sb, 0, 0);
|
||||
if (error) {
|
||||
xfs_trans_cancel(tp, 0);
|
||||
return error;
|
||||
}
|
||||
xfs_mod_sb(tp, fields);
|
||||
error = xfs_trans_commit(tp, 0);
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the underlying (data/log/rt) device is readonly, there are some
|
||||
* operations that cannot proceed.
|
||||
|
|
|
@ -162,8 +162,7 @@ typedef struct xfs_mount {
|
|||
struct delayed_work m_reclaim_work; /* background inode reclaim */
|
||||
struct delayed_work m_eofblocks_work; /* background eof blocks
|
||||
trimming */
|
||||
__int64_t m_update_flags; /* sb flags we need to update
|
||||
on the next remount,rw */
|
||||
bool m_update_sb; /* sb needs update in mount */
|
||||
int64_t m_low_space[XFS_LOWSP_MAX];
|
||||
/* low free space thresholds */
|
||||
struct xfs_kobj m_kobj;
|
||||
|
@ -378,7 +377,7 @@ extern void xfs_unmountfs(xfs_mount_t *);
|
|||
extern int xfs_mod_incore_sb(xfs_mount_t *, xfs_sb_field_t, int64_t, int);
|
||||
extern int xfs_mod_incore_sb_batch(xfs_mount_t *, xfs_mod_sb_t *,
|
||||
uint, int);
|
||||
extern int xfs_mount_log_sb(xfs_mount_t *, __int64_t);
|
||||
extern int xfs_mount_log_sb(xfs_mount_t *);
|
||||
extern struct xfs_buf *xfs_getsb(xfs_mount_t *, int);
|
||||
extern int xfs_readsb(xfs_mount_t *, int);
|
||||
extern void xfs_freesb(xfs_mount_t *);
|
||||
|
|
|
@ -714,7 +714,6 @@ STATIC int
|
|||
xfs_qm_qino_alloc(
|
||||
xfs_mount_t *mp,
|
||||
xfs_inode_t **ip,
|
||||
__int64_t sbfields,
|
||||
uint flags)
|
||||
{
|
||||
xfs_trans_t *tp;
|
||||
|
@ -777,11 +776,6 @@ xfs_qm_qino_alloc(
|
|||
spin_lock(&mp->m_sb_lock);
|
||||
if (flags & XFS_QMOPT_SBVERSION) {
|
||||
ASSERT(!xfs_sb_version_hasquota(&mp->m_sb));
|
||||
ASSERT((sbfields & (XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO |
|
||||
XFS_SB_GQUOTINO | XFS_SB_PQUOTINO | XFS_SB_QFLAGS)) ==
|
||||
(XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO |
|
||||
XFS_SB_GQUOTINO | XFS_SB_PQUOTINO |
|
||||
XFS_SB_QFLAGS));
|
||||
|
||||
xfs_sb_version_addquota(&mp->m_sb);
|
||||
mp->m_sb.sb_uquotino = NULLFSINO;
|
||||
|
@ -798,7 +792,7 @@ xfs_qm_qino_alloc(
|
|||
else
|
||||
mp->m_sb.sb_pquotino = (*ip)->i_ino;
|
||||
spin_unlock(&mp->m_sb_lock);
|
||||
xfs_mod_sb(tp, sbfields);
|
||||
xfs_log_sb(tp);
|
||||
|
||||
if ((error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES))) {
|
||||
xfs_alert(mp, "%s failed (error %d)!", __func__, error);
|
||||
|
@ -1451,7 +1445,7 @@ xfs_qm_mount_quotas(
|
|||
spin_unlock(&mp->m_sb_lock);
|
||||
|
||||
if (sbf != (mp->m_qflags & XFS_MOUNT_QUOTA_ALL)) {
|
||||
if (xfs_qm_write_sb_changes(mp, XFS_SB_QFLAGS)) {
|
||||
if (xfs_sync_sb(mp, false)) {
|
||||
/*
|
||||
* We could only have been turning quotas off.
|
||||
* We aren't in very good shape actually because
|
||||
|
@ -1482,7 +1476,6 @@ xfs_qm_init_quotainos(
|
|||
struct xfs_inode *gip = NULL;
|
||||
struct xfs_inode *pip = NULL;
|
||||
int error;
|
||||
__int64_t sbflags = 0;
|
||||
uint flags = 0;
|
||||
|
||||
ASSERT(mp->m_quotainfo);
|
||||
|
@ -1517,9 +1510,6 @@ xfs_qm_init_quotainos(
|
|||
}
|
||||
} else {
|
||||
flags |= XFS_QMOPT_SBVERSION;
|
||||
sbflags |= (XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO |
|
||||
XFS_SB_GQUOTINO | XFS_SB_PQUOTINO |
|
||||
XFS_SB_QFLAGS);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1530,7 +1520,6 @@ xfs_qm_init_quotainos(
|
|||
*/
|
||||
if (XFS_IS_UQUOTA_ON(mp) && uip == NULL) {
|
||||
error = xfs_qm_qino_alloc(mp, &uip,
|
||||
sbflags | XFS_SB_UQUOTINO,
|
||||
flags | XFS_QMOPT_UQUOTA);
|
||||
if (error)
|
||||
goto error_rele;
|
||||
|
@ -1539,7 +1528,6 @@ xfs_qm_init_quotainos(
|
|||
}
|
||||
if (XFS_IS_GQUOTA_ON(mp) && gip == NULL) {
|
||||
error = xfs_qm_qino_alloc(mp, &gip,
|
||||
sbflags | XFS_SB_GQUOTINO,
|
||||
flags | XFS_QMOPT_GQUOTA);
|
||||
if (error)
|
||||
goto error_rele;
|
||||
|
@ -1548,7 +1536,6 @@ xfs_qm_init_quotainos(
|
|||
}
|
||||
if (XFS_IS_PQUOTA_ON(mp) && pip == NULL) {
|
||||
error = xfs_qm_qino_alloc(mp, &pip,
|
||||
sbflags | XFS_SB_PQUOTINO,
|
||||
flags | XFS_QMOPT_PQUOTA);
|
||||
if (error)
|
||||
goto error_rele;
|
||||
|
@ -1587,32 +1574,6 @@ xfs_qm_dqfree_one(
|
|||
xfs_qm_dqdestroy(dqp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Start a transaction and write the incore superblock changes to
|
||||
* disk. flags parameter indicates which fields have changed.
|
||||
*/
|
||||
int
|
||||
xfs_qm_write_sb_changes(
|
||||
xfs_mount_t *mp,
|
||||
__int64_t flags)
|
||||
{
|
||||
xfs_trans_t *tp;
|
||||
int error;
|
||||
|
||||
tp = xfs_trans_alloc(mp, XFS_TRANS_QM_SBCHANGE);
|
||||
error = xfs_trans_reserve(tp, &M_RES(mp)->tr_qm_sbchange, 0, 0);
|
||||
if (error) {
|
||||
xfs_trans_cancel(tp, 0);
|
||||
return error;
|
||||
}
|
||||
|
||||
xfs_mod_sb(tp, flags);
|
||||
error = xfs_trans_commit(tp, 0);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
/* --------------- utility functions for vnodeops ---------------- */
|
||||
|
||||
|
||||
|
|
|
@ -157,7 +157,6 @@ struct xfs_dquot_acct {
|
|||
#define XFS_QM_RTBWARNLIMIT 5
|
||||
|
||||
extern void xfs_qm_destroy_quotainfo(struct xfs_mount *);
|
||||
extern int xfs_qm_write_sb_changes(struct xfs_mount *, __int64_t);
|
||||
|
||||
/* dquot stuff */
|
||||
extern void xfs_qm_dqpurge_all(struct xfs_mount *, uint);
|
||||
|
|
|
@ -91,8 +91,7 @@ xfs_qm_scall_quotaoff(
|
|||
mutex_unlock(&q->qi_quotaofflock);
|
||||
|
||||
/* XXX what to do if error ? Revert back to old vals incore ? */
|
||||
error = xfs_qm_write_sb_changes(mp, XFS_SB_QFLAGS);
|
||||
return error;
|
||||
return xfs_sync_sb(mp, false);
|
||||
}
|
||||
|
||||
dqtype = 0;
|
||||
|
@ -313,7 +312,6 @@ xfs_qm_scall_quotaon(
|
|||
{
|
||||
int error;
|
||||
uint qf;
|
||||
__int64_t sbflags;
|
||||
|
||||
flags &= (XFS_ALL_QUOTA_ACCT | XFS_ALL_QUOTA_ENFD);
|
||||
/*
|
||||
|
@ -321,8 +319,6 @@ xfs_qm_scall_quotaon(
|
|||
*/
|
||||
flags &= ~(XFS_ALL_QUOTA_ACCT);
|
||||
|
||||
sbflags = 0;
|
||||
|
||||
if (flags == 0) {
|
||||
xfs_debug(mp, "%s: zero flags, m_qflags=%x",
|
||||
__func__, mp->m_qflags);
|
||||
|
@ -363,11 +359,11 @@ xfs_qm_scall_quotaon(
|
|||
/*
|
||||
* There's nothing to change if it's the same.
|
||||
*/
|
||||
if ((qf & flags) == flags && sbflags == 0)
|
||||
if ((qf & flags) == flags)
|
||||
return -EEXIST;
|
||||
sbflags |= XFS_SB_QFLAGS;
|
||||
|
||||
if ((error = xfs_qm_write_sb_changes(mp, sbflags)))
|
||||
error = xfs_sync_sb(mp, false);
|
||||
if (error)
|
||||
return error;
|
||||
/*
|
||||
* If we aren't trying to switch on quota enforcement, we are done.
|
||||
|
@ -774,7 +770,7 @@ xfs_qm_log_quotaoff(
|
|||
mp->m_sb.sb_qflags = (mp->m_qflags & ~(flags)) & XFS_MOUNT_QUOTA_ALL;
|
||||
spin_unlock(&mp->m_sb_lock);
|
||||
|
||||
xfs_mod_sb(tp, XFS_SB_QFLAGS);
|
||||
xfs_log_sb(tp);
|
||||
|
||||
/*
|
||||
* We have to make sure that the transaction is secure on disk before we
|
||||
|
|
|
@ -685,7 +685,7 @@ xfs_blkdev_get(
|
|||
mp);
|
||||
if (IS_ERR(*bdevp)) {
|
||||
error = PTR_ERR(*bdevp);
|
||||
xfs_warn(mp, "Invalid device [%s], error=%d\n", name, error);
|
||||
xfs_warn(mp, "Invalid device [%s], error=%d", name, error);
|
||||
}
|
||||
|
||||
return error;
|
||||
|
@ -1111,6 +1111,11 @@ xfs_fs_statfs(
|
|||
statp->f_files,
|
||||
mp->m_maxicount);
|
||||
|
||||
/* If sb_icount overshot maxicount, report actual allocation */
|
||||
statp->f_files = max_t(typeof(statp->f_files),
|
||||
statp->f_files,
|
||||
sbp->sb_icount);
|
||||
|
||||
/* make sure statp->f_ffree does not underflow */
|
||||
ffree = statp->f_files - (sbp->sb_icount - sbp->sb_ifree);
|
||||
statp->f_ffree = max_t(__int64_t, ffree, 0);
|
||||
|
@ -1257,13 +1262,13 @@ xfs_fs_remount(
|
|||
* If this is the first remount to writeable state we
|
||||
* might have some superblock changes to update.
|
||||
*/
|
||||
if (mp->m_update_flags) {
|
||||
error = xfs_mount_log_sb(mp, mp->m_update_flags);
|
||||
if (mp->m_update_sb) {
|
||||
error = xfs_sync_sb(mp, false);
|
||||
if (error) {
|
||||
xfs_warn(mp, "failed to write sb changes");
|
||||
return error;
|
||||
}
|
||||
mp->m_update_flags = 0;
|
||||
mp->m_update_sb = false;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1293,8 +1298,9 @@ xfs_fs_remount(
|
|||
|
||||
/*
|
||||
* Second stage of a freeze. The data is already frozen so we only
|
||||
* need to take care of the metadata. Once that's done write a dummy
|
||||
* record to dirty the log in case of a crash while frozen.
|
||||
* need to take care of the metadata. Once that's done sync the superblock
|
||||
* to the log to dirty it in case of a crash while frozen. This ensures that we
|
||||
* will recover the unlinked inode lists on the next mount.
|
||||
*/
|
||||
STATIC int
|
||||
xfs_fs_freeze(
|
||||
|
@ -1304,7 +1310,7 @@ xfs_fs_freeze(
|
|||
|
||||
xfs_save_resvblks(mp);
|
||||
xfs_quiesce_attr(mp);
|
||||
return xfs_fs_log_dummy(mp);
|
||||
return xfs_sync_sb(mp, true);
|
||||
}
|
||||
|
||||
STATIC int
|
||||
|
|
|
@ -148,24 +148,6 @@ static struct ctl_table xfs_table[] = {
|
|||
.extra1 = &xfs_params.inherit_noatim.min,
|
||||
.extra2 = &xfs_params.inherit_noatim.max
|
||||
},
|
||||
{
|
||||
.procname = "xfsbufd_centisecs",
|
||||
.data = &xfs_params.xfs_buf_timer.val,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = &xfs_params.xfs_buf_timer.min,
|
||||
.extra2 = &xfs_params.xfs_buf_timer.max
|
||||
},
|
||||
{
|
||||
.procname = "age_buffer_centisecs",
|
||||
.data = &xfs_params.xfs_buf_age.val,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = proc_dointvec_minmax,
|
||||
.extra1 = &xfs_params.xfs_buf_age.min,
|
||||
.extra2 = &xfs_params.xfs_buf_age.max
|
||||
},
|
||||
{
|
||||
.procname = "inherit_nosymlinks",
|
||||
.data = &xfs_params.inherit_nosym.val,
|
||||
|
|
|
@ -472,6 +472,7 @@ xfs_trans_apply_sb_deltas(
|
|||
whole = 1;
|
||||
}
|
||||
|
||||
xfs_trans_buf_set_type(tp, bp, XFS_BLFT_SB_BUF);
|
||||
if (whole)
|
||||
/*
|
||||
* Log the whole thing, the fields are noncontiguous.
|
||||
|
|
|
@ -327,9 +327,10 @@ xfs_trans_read_buf_map(
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
if (tp)
|
||||
if (tp) {
|
||||
_xfs_trans_bjoin(tp, bp, 1);
|
||||
trace_xfs_trans_read_buf(bp->b_fspriv);
|
||||
trace_xfs_trans_read_buf(bp->b_fspriv);
|
||||
}
|
||||
*bpp = bp;
|
||||
return 0;
|
||||
|
||||
|
|
Loading…
Reference in a new issue