a010b40938
Signed-off-by: Stoiko Ivanov <s.ivanov@proxmox.com>
868 lines
27 KiB
Diff
868 lines
27 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Chunwei Chen <tuxoko@gmail.com>
|
|
Date: Wed, 18 Apr 2018 14:19:50 -0700
|
|
Subject: [PATCH] Fix ENOSPC in "Handle zap_add() failures in ..."
|
|
|
|
Commit cc63068 caused ENOSPC error when copy a large amount of files
|
|
between two directories. The reason is that the patch limits zap leaf
|
|
expansion to 2 retries, and return ENOSPC when failed.
|
|
|
|
The intent for limiting retries is to prevent pointlessly growing table
|
|
to max size when adding a block full of entries with same name in
|
|
different case in mixed mode. However, it turns out we cannot use any
|
|
limit on the retry. When we copy files from one directory in readdir
|
|
order, we are copying in hash order, one leaf block at a time. Which
|
|
means that if the leaf block in source directory has expanded 6 times,
|
|
and you copy those entries in that block, by the time you need to expand
|
|
the leaf in destination directory, you need to expand it 6 times in one
|
|
go. So any limit on the retry will result in error where it shouldn't.
|
|
|
|
Note that while we do use different salt for different directories, it
|
|
seems that the salt/hash function doesn't provide enough randomization
|
|
to the hash distance to prevent this from happening.
|
|
|
|
Since cc63068 has already been reverted. This patch adds it back and
|
|
removes the retry limit.
|
|
|
|
Also, as it turn out, failing on zap_add() has a serious side effect for
|
|
mzap_upgrade(). When upgrading from micro zap to fat zap, it will
|
|
call zap_add() to transfer entries one at a time. If it hit any error
|
|
halfway through, the remaining entries will be lost, causing those files
|
|
to become orphan. This patch add a VERIFY to catch it.
|
|
|
|
Reviewed-by: Sanjeev Bagewadi <sanjeev.bagewadi@gmail.com>
|
|
Reviewed-by: Richard Yao <ryao@gentoo.org>
|
|
Reviewed-by: Tony Hutter <hutter2@llnl.gov>
|
|
Reviewed-by: Albert Lee <trisk@forkgnu.org>
|
|
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
|
|
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
|
|
Signed-off-by: Chunwei Chen <david.chen@nutanix.com>
|
|
Closes #7401
|
|
Closes #7421
|
|
|
|
Signed-off-by: Stoiko Ivanov <s.ivanov@proxmox.com>
|
|
---
|
|
configure.ac | 1 +
|
|
include/sys/zap_leaf.h | 15 ++-
|
|
module/zfs/zap.c | 10 +-
|
|
module/zfs/zap_leaf.c | 2 +-
|
|
module/zfs/zap_micro.c | 47 ++++++-
|
|
module/zfs/zfs_dir.c | 29 ++++-
|
|
module/zfs/zfs_vnops.c | 74 ++++++++---
|
|
tests/runfiles/linux.run | 6 +-
|
|
tests/zfs-tests/tests/functional/Makefile.am | 1 +
|
|
.../tests/functional/casenorm/Makefile.am | 1 +
|
|
.../functional/casenorm/mixed_create_failure.ksh | 136 +++++++++++++++++++++
|
|
.../zfs-tests/tests/functional/cp_files/.gitignore | 1 +
|
|
.../tests/functional/cp_files/Makefile.am | 13 ++
|
|
.../tests/functional/cp_files/cleanup.ksh | 34 ++++++
|
|
.../zfs-tests/tests/functional/cp_files/cp_files.c | 58 +++++++++
|
|
.../tests/functional/cp_files/cp_files_001_pos.ksh | 74 +++++++++++
|
|
.../zfs-tests/tests/functional/cp_files/setup.ksh | 35 ++++++
|
|
17 files changed, 500 insertions(+), 37 deletions(-)
|
|
create mode 100755 tests/zfs-tests/tests/functional/casenorm/mixed_create_failure.ksh
|
|
create mode 100644 tests/zfs-tests/tests/functional/cp_files/.gitignore
|
|
create mode 100644 tests/zfs-tests/tests/functional/cp_files/Makefile.am
|
|
create mode 100755 tests/zfs-tests/tests/functional/cp_files/cleanup.ksh
|
|
create mode 100644 tests/zfs-tests/tests/functional/cp_files/cp_files.c
|
|
create mode 100755 tests/zfs-tests/tests/functional/cp_files/cp_files_001_pos.ksh
|
|
create mode 100755 tests/zfs-tests/tests/functional/cp_files/setup.ksh
|
|
|
|
diff --git a/configure.ac b/configure.ac
|
|
index d9441a0f..3f4925c3 100644
|
|
--- a/configure.ac
|
|
+++ b/configure.ac
|
|
@@ -238,6 +238,7 @@ AC_CONFIG_FILES([
|
|
tests/zfs-tests/tests/functional/cli_user/zpool_iostat/Makefile
|
|
tests/zfs-tests/tests/functional/cli_user/zpool_list/Makefile
|
|
tests/zfs-tests/tests/functional/compression/Makefile
|
|
+ tests/zfs-tests/tests/functional/cp_files/Makefile
|
|
tests/zfs-tests/tests/functional/ctime/Makefile
|
|
tests/zfs-tests/tests/functional/delegate/Makefile
|
|
tests/zfs-tests/tests/functional/devices/Makefile
|
|
diff --git a/include/sys/zap_leaf.h b/include/sys/zap_leaf.h
|
|
index e784c596..a3da1036 100644
|
|
--- a/include/sys/zap_leaf.h
|
|
+++ b/include/sys/zap_leaf.h
|
|
@@ -46,10 +46,15 @@ struct zap_stats;
|
|
* block size (1<<l->l_bs) - hash entry size (2) * number of hash
|
|
* entries - header space (2*chunksize)
|
|
*/
|
|
-#define ZAP_LEAF_NUMCHUNKS(l) \
|
|
- (((1<<(l)->l_bs) - 2*ZAP_LEAF_HASH_NUMENTRIES(l)) / \
|
|
+#define ZAP_LEAF_NUMCHUNKS_BS(bs) \
|
|
+ (((1<<(bs)) - 2*ZAP_LEAF_HASH_NUMENTRIES_BS(bs)) / \
|
|
ZAP_LEAF_CHUNKSIZE - 2)
|
|
|
|
+#define ZAP_LEAF_NUMCHUNKS(l) (ZAP_LEAF_NUMCHUNKS_BS(((l)->l_bs)))
|
|
+
|
|
+#define ZAP_LEAF_NUMCHUNKS_DEF \
|
|
+ (ZAP_LEAF_NUMCHUNKS_BS(fzap_default_block_shift))
|
|
+
|
|
/*
|
|
* The amount of space within the chunk available for the array is:
|
|
* chunk size - space for type (1) - space for next pointer (2)
|
|
@@ -74,8 +79,10 @@ struct zap_stats;
|
|
* which is less than block size / CHUNKSIZE (24) / minimum number of
|
|
* chunks per entry (3).
|
|
*/
|
|
-#define ZAP_LEAF_HASH_SHIFT(l) ((l)->l_bs - 5)
|
|
-#define ZAP_LEAF_HASH_NUMENTRIES(l) (1 << ZAP_LEAF_HASH_SHIFT(l))
|
|
+#define ZAP_LEAF_HASH_SHIFT_BS(bs) ((bs) - 5)
|
|
+#define ZAP_LEAF_HASH_NUMENTRIES_BS(bs) (1 << ZAP_LEAF_HASH_SHIFT_BS(bs))
|
|
+#define ZAP_LEAF_HASH_SHIFT(l) (ZAP_LEAF_HASH_SHIFT_BS(((l)->l_bs)))
|
|
+#define ZAP_LEAF_HASH_NUMENTRIES(l) (ZAP_LEAF_HASH_NUMENTRIES_BS(((l)->l_bs)))
|
|
|
|
/*
|
|
* The chunks start immediately after the hash table. The end of the
|
|
diff --git a/module/zfs/zap.c b/module/zfs/zap.c
|
|
index ee9962bf..47b4c1ab 100644
|
|
--- a/module/zfs/zap.c
|
|
+++ b/module/zfs/zap.c
|
|
@@ -853,8 +853,16 @@ retry:
|
|
} else if (err == EAGAIN) {
|
|
err = zap_expand_leaf(zn, l, tag, tx, &l);
|
|
zap = zn->zn_zap; /* zap_expand_leaf() may change zap */
|
|
- if (err == 0)
|
|
+ if (err == 0) {
|
|
goto retry;
|
|
+ } else if (err == ENOSPC) {
|
|
+ /*
|
|
+ * If we failed to expand the leaf, then bailout
|
|
+ * as there is no point trying
|
|
+ * zap_put_leaf_maybe_grow_ptrtbl().
|
|
+ */
|
|
+ return (err);
|
|
+ }
|
|
}
|
|
|
|
out:
|
|
diff --git a/module/zfs/zap_leaf.c b/module/zfs/zap_leaf.c
|
|
index c342695c..526e4660 100644
|
|
--- a/module/zfs/zap_leaf.c
|
|
+++ b/module/zfs/zap_leaf.c
|
|
@@ -53,7 +53,7 @@ static uint16_t *zap_leaf_rehash_entry(zap_leaf_t *l, uint16_t entry);
|
|
((h) >> \
|
|
(64 - ZAP_LEAF_HASH_SHIFT(l) - zap_leaf_phys(l)->l_hdr.lh_prefix_len)))
|
|
|
|
-#define LEAF_HASH_ENTPTR(l, h) (&zap_leaf_phys(l)->l_hash[LEAF_HASH(l, h)])
|
|
+#define LEAF_HASH_ENTPTR(l, h) (&zap_leaf_phys(l)->l_hash[LEAF_HASH(l, h)])
|
|
|
|
extern inline zap_leaf_phys_t *zap_leaf_phys(zap_leaf_t *l);
|
|
|
|
diff --git a/module/zfs/zap_micro.c b/module/zfs/zap_micro.c
|
|
index 3ebf995c..60e193ef 100644
|
|
--- a/module/zfs/zap_micro.c
|
|
+++ b/module/zfs/zap_micro.c
|
|
@@ -363,6 +363,41 @@ mze_find_unused_cd(zap_t *zap, uint64_t hash)
|
|
return (cd);
|
|
}
|
|
|
|
+/*
|
|
+ * Each mzap entry requires at max : 4 chunks
|
|
+ * 3 chunks for names + 1 chunk for value.
|
|
+ */
|
|
+#define MZAP_ENT_CHUNKS (1 + ZAP_LEAF_ARRAY_NCHUNKS(MZAP_NAME_LEN) + \
|
|
+ ZAP_LEAF_ARRAY_NCHUNKS(sizeof (uint64_t)))
|
|
+
|
|
+/*
|
|
+ * Check if the current entry keeps the colliding entries under the fatzap leaf
|
|
+ * size.
|
|
+ */
|
|
+static boolean_t
|
|
+mze_canfit_fzap_leaf(zap_name_t *zn, uint64_t hash)
|
|
+{
|
|
+ zap_t *zap = zn->zn_zap;
|
|
+ mzap_ent_t mze_tofind;
|
|
+ mzap_ent_t *mze;
|
|
+ avl_index_t idx;
|
|
+ avl_tree_t *avl = &zap->zap_m.zap_avl;
|
|
+ uint32_t mzap_ents = 0;
|
|
+
|
|
+ mze_tofind.mze_hash = hash;
|
|
+ mze_tofind.mze_cd = 0;
|
|
+
|
|
+ for (mze = avl_find(avl, &mze_tofind, &idx);
|
|
+ mze && mze->mze_hash == hash; mze = AVL_NEXT(avl, mze)) {
|
|
+ mzap_ents++;
|
|
+ }
|
|
+
|
|
+ /* Include the new entry being added */
|
|
+ mzap_ents++;
|
|
+
|
|
+ return (ZAP_LEAF_NUMCHUNKS_DEF > (mzap_ents * MZAP_ENT_CHUNKS));
|
|
+}
|
|
+
|
|
static void
|
|
mze_remove(zap_t *zap, mzap_ent_t *mze)
|
|
{
|
|
@@ -639,16 +674,15 @@ mzap_upgrade(zap_t **zapp, void *tag, dmu_tx_t *tx, zap_flags_t flags)
|
|
dprintf("adding %s=%llu\n",
|
|
mze->mze_name, mze->mze_value);
|
|
zn = zap_name_alloc(zap, mze->mze_name, 0);
|
|
- err = fzap_add_cd(zn, 8, 1, &mze->mze_value, mze->mze_cd,
|
|
- tag, tx);
|
|
+ /* If we fail here, we would end up losing entries */
|
|
+ VERIFY0(fzap_add_cd(zn, 8, 1, &mze->mze_value, mze->mze_cd,
|
|
+ tag, tx));
|
|
zap = zn->zn_zap; /* fzap_add_cd() may change zap */
|
|
zap_name_free(zn);
|
|
- if (err)
|
|
- break;
|
|
}
|
|
vmem_free(mzp, sz);
|
|
*zapp = zap;
|
|
- return (err);
|
|
+ return (0);
|
|
}
|
|
|
|
/*
|
|
@@ -1191,7 +1225,8 @@ zap_add_impl(zap_t *zap, const char *key,
|
|
err = fzap_add(zn, integer_size, num_integers, val, tag, tx);
|
|
zap = zn->zn_zap; /* fzap_add() may change zap */
|
|
} else if (integer_size != 8 || num_integers != 1 ||
|
|
- strlen(key) >= MZAP_NAME_LEN) {
|
|
+ strlen(key) >= MZAP_NAME_LEN ||
|
|
+ !mze_canfit_fzap_leaf(zn, zn->zn_hash)) {
|
|
err = mzap_upgrade(&zn->zn_zap, tag, tx, 0);
|
|
if (err == 0) {
|
|
err = fzap_add(zn, integer_size, num_integers, val,
|
|
diff --git a/module/zfs/zfs_dir.c b/module/zfs/zfs_dir.c
|
|
index 9a8bbccd..6398a1d1 100644
|
|
--- a/module/zfs/zfs_dir.c
|
|
+++ b/module/zfs/zfs_dir.c
|
|
@@ -742,7 +742,11 @@ zfs_dirent(znode_t *zp, uint64_t mode)
|
|
}
|
|
|
|
/*
|
|
- * Link zp into dl. Can only fail if zp has been unlinked.
|
|
+ * Link zp into dl. Can fail in the following cases :
|
|
+ * - if zp has been unlinked.
|
|
+ * - if the number of entries with the same hash (aka. colliding entries)
|
|
+ * exceed the capacity of a leaf-block of fatzap and splitting of the
|
|
+ * leaf-block does not help.
|
|
*/
|
|
int
|
|
zfs_link_create(zfs_dirlock_t *dl, znode_t *zp, dmu_tx_t *tx, int flag)
|
|
@@ -776,6 +780,24 @@ zfs_link_create(zfs_dirlock_t *dl, znode_t *zp, dmu_tx_t *tx, int flag)
|
|
NULL, &links, sizeof (links));
|
|
}
|
|
}
|
|
+
|
|
+ value = zfs_dirent(zp, zp->z_mode);
|
|
+ error = zap_add(ZTOZSB(zp)->z_os, dzp->z_id, dl->dl_name, 8, 1,
|
|
+ &value, tx);
|
|
+
|
|
+ /*
|
|
+ * zap_add could fail to add the entry if it exceeds the capacity of the
|
|
+ * leaf-block and zap_leaf_split() failed to help.
|
|
+ * The caller of this routine is responsible for failing the transaction
|
|
+ * which will rollback the SA updates done above.
|
|
+ */
|
|
+ if (error != 0) {
|
|
+ if (!(flag & ZRENAMING) && !(flag & ZNEW))
|
|
+ drop_nlink(ZTOI(zp));
|
|
+ mutex_exit(&zp->z_lock);
|
|
+ return (error);
|
|
+ }
|
|
+
|
|
SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_PARENT(zfsvfs), NULL,
|
|
&dzp->z_id, sizeof (dzp->z_id));
|
|
SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_FLAGS(zfsvfs), NULL,
|
|
@@ -813,11 +835,6 @@ zfs_link_create(zfs_dirlock_t *dl, znode_t *zp, dmu_tx_t *tx, int flag)
|
|
ASSERT(error == 0);
|
|
mutex_exit(&dzp->z_lock);
|
|
|
|
- value = zfs_dirent(zp, zp->z_mode);
|
|
- error = zap_add(ZTOZSB(zp)->z_os, dzp->z_id, dl->dl_name,
|
|
- 8, 1, &value, tx);
|
|
- ASSERT(error == 0);
|
|
-
|
|
return (0);
|
|
}
|
|
|
|
diff --git a/module/zfs/zfs_vnops.c b/module/zfs/zfs_vnops.c
|
|
index 4805f897..5a2e55eb 100644
|
|
--- a/module/zfs/zfs_vnops.c
|
|
+++ b/module/zfs/zfs_vnops.c
|
|
@@ -1427,6 +1427,7 @@ top:
|
|
dmu_tx_hold_write(tx, DMU_NEW_OBJECT,
|
|
0, acl_ids.z_aclp->z_acl_bytes);
|
|
}
|
|
+
|
|
error = dmu_tx_assign(tx,
|
|
(waited ? TXG_NOTHROTTLE : 0) | TXG_NOWAIT);
|
|
if (error) {
|
|
@@ -1444,10 +1445,22 @@ top:
|
|
}
|
|
zfs_mknode(dzp, vap, tx, cr, 0, &zp, &acl_ids);
|
|
|
|
+ error = zfs_link_create(dl, zp, tx, ZNEW);
|
|
+ if (error != 0) {
|
|
+ /*
|
|
+ * Since, we failed to add the directory entry for it,
|
|
+ * delete the newly created dnode.
|
|
+ */
|
|
+ zfs_znode_delete(zp, tx);
|
|
+ remove_inode_hash(ZTOI(zp));
|
|
+ zfs_acl_ids_free(&acl_ids);
|
|
+ dmu_tx_commit(tx);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
if (fuid_dirtied)
|
|
zfs_fuid_sync(zfsvfs, tx);
|
|
|
|
- (void) zfs_link_create(dl, zp, tx, ZNEW);
|
|
txtype = zfs_log_create_txtype(Z_FILE, vsecp, vap);
|
|
if (flag & FIGNORECASE)
|
|
txtype |= TX_CI;
|
|
@@ -2038,13 +2051,18 @@ top:
|
|
*/
|
|
zfs_mknode(dzp, vap, tx, cr, 0, &zp, &acl_ids);
|
|
|
|
- if (fuid_dirtied)
|
|
- zfs_fuid_sync(zfsvfs, tx);
|
|
-
|
|
/*
|
|
* Now put new name in parent dir.
|
|
*/
|
|
- (void) zfs_link_create(dl, zp, tx, ZNEW);
|
|
+ error = zfs_link_create(dl, zp, tx, ZNEW);
|
|
+ if (error != 0) {
|
|
+ zfs_znode_delete(zp, tx);
|
|
+ remove_inode_hash(ZTOI(zp));
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ if (fuid_dirtied)
|
|
+ zfs_fuid_sync(zfsvfs, tx);
|
|
|
|
*ipp = ZTOI(zp);
|
|
|
|
@@ -2054,6 +2072,7 @@ top:
|
|
zfs_log_create(zilog, tx, txtype, dzp, zp, dirname, vsecp,
|
|
acl_ids.z_fuidp, vap);
|
|
|
|
+out:
|
|
zfs_acl_ids_free(&acl_ids);
|
|
|
|
dmu_tx_commit(tx);
|
|
@@ -2063,10 +2082,14 @@ top:
|
|
if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS)
|
|
zil_commit(zilog, 0);
|
|
|
|
- zfs_inode_update(dzp);
|
|
- zfs_inode_update(zp);
|
|
+ if (error != 0) {
|
|
+ iput(ZTOI(zp));
|
|
+ } else {
|
|
+ zfs_inode_update(dzp);
|
|
+ zfs_inode_update(zp);
|
|
+ }
|
|
ZFS_EXIT(zfsvfs);
|
|
- return (0);
|
|
+ return (error);
|
|
}
|
|
|
|
/*
|
|
@@ -3684,6 +3707,13 @@ top:
|
|
VERIFY3U(zfs_link_destroy(tdl, szp, tx,
|
|
ZRENAMING, NULL), ==, 0);
|
|
}
|
|
+ } else {
|
|
+ /*
|
|
+ * If we had removed the existing target, subsequent
|
|
+ * call to zfs_link_create() to add back the same entry
|
|
+ * but, the new dnode (szp) should not fail.
|
|
+ */
|
|
+ ASSERT(tzp == NULL);
|
|
}
|
|
}
|
|
|
|
@@ -3854,14 +3884,18 @@ top:
|
|
/*
|
|
* Insert the new object into the directory.
|
|
*/
|
|
- (void) zfs_link_create(dl, zp, tx, ZNEW);
|
|
-
|
|
- if (flags & FIGNORECASE)
|
|
- txtype |= TX_CI;
|
|
- zfs_log_symlink(zilog, tx, txtype, dzp, zp, name, link);
|
|
+ error = zfs_link_create(dl, zp, tx, ZNEW);
|
|
+ if (error != 0) {
|
|
+ zfs_znode_delete(zp, tx);
|
|
+ remove_inode_hash(ZTOI(zp));
|
|
+ } else {
|
|
+ if (flags & FIGNORECASE)
|
|
+ txtype |= TX_CI;
|
|
+ zfs_log_symlink(zilog, tx, txtype, dzp, zp, name, link);
|
|
|
|
- zfs_inode_update(dzp);
|
|
- zfs_inode_update(zp);
|
|
+ zfs_inode_update(dzp);
|
|
+ zfs_inode_update(zp);
|
|
+ }
|
|
|
|
zfs_acl_ids_free(&acl_ids);
|
|
|
|
@@ -3869,10 +3903,14 @@ top:
|
|
|
|
zfs_dirent_unlock(dl);
|
|
|
|
- *ipp = ZTOI(zp);
|
|
+ if (error == 0) {
|
|
+ *ipp = ZTOI(zp);
|
|
|
|
- if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS)
|
|
- zil_commit(zilog, 0);
|
|
+ if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS)
|
|
+ zil_commit(zilog, 0);
|
|
+ } else {
|
|
+ iput(ZTOI(zp));
|
|
+ }
|
|
|
|
ZFS_EXIT(zfsvfs);
|
|
return (error);
|
|
diff --git a/tests/runfiles/linux.run b/tests/runfiles/linux.run
|
|
index 272c8c77..379c9f73 100644
|
|
--- a/tests/runfiles/linux.run
|
|
+++ b/tests/runfiles/linux.run
|
|
@@ -55,7 +55,7 @@ tags = ['functional', 'cachefile']
|
|
# 'mixed_none_lookup', 'mixed_none_lookup_ci', 'mixed_none_delete',
|
|
# 'mixed_formd_lookup', 'mixed_formd_lookup_ci', 'mixed_formd_delete']
|
|
[tests/functional/casenorm]
|
|
-tests = ['case_all_values', 'norm_all_values']
|
|
+tests = ['case_all_values', 'norm_all_values', 'mixed_create_failure']
|
|
tags = ['functional', 'casenorm']
|
|
|
|
[tests/functional/chattr]
|
|
@@ -394,6 +394,10 @@ tests = ['compress_001_pos', 'compress_002_pos', 'compress_003_pos',
|
|
'compress_004_pos']
|
|
tags = ['functional', 'compression']
|
|
|
|
+[tests/functional/cp_files]
|
|
+tests = ['cp_files_001_pos']
|
|
+tags = ['functional', 'cp_files']
|
|
+
|
|
[tests/functional/ctime]
|
|
tests = ['ctime_001_pos' ]
|
|
tags = ['functional', 'ctime']
|
|
diff --git a/tests/zfs-tests/tests/functional/Makefile.am b/tests/zfs-tests/tests/functional/Makefile.am
|
|
index cd60324f..ea52205a 100644
|
|
--- a/tests/zfs-tests/tests/functional/Makefile.am
|
|
+++ b/tests/zfs-tests/tests/functional/Makefile.am
|
|
@@ -11,6 +11,7 @@ SUBDIRS = \
|
|
cli_root \
|
|
cli_user \
|
|
compression \
|
|
+ cp_files \
|
|
ctime \
|
|
delegate \
|
|
devices \
|
|
diff --git a/tests/zfs-tests/tests/functional/casenorm/Makefile.am b/tests/zfs-tests/tests/functional/casenorm/Makefile.am
|
|
index 65dd156e..b284a256 100644
|
|
--- a/tests/zfs-tests/tests/functional/casenorm/Makefile.am
|
|
+++ b/tests/zfs-tests/tests/functional/casenorm/Makefile.am
|
|
@@ -7,6 +7,7 @@ dist_pkgdata_SCRIPTS = \
|
|
insensitive_formd_lookup.ksh \
|
|
insensitive_none_delete.ksh \
|
|
insensitive_none_lookup.ksh \
|
|
+ mixed_create_failure.ksh \
|
|
mixed_formd_delete.ksh \
|
|
mixed_formd_lookup_ci.ksh \
|
|
mixed_formd_lookup.ksh \
|
|
diff --git a/tests/zfs-tests/tests/functional/casenorm/mixed_create_failure.ksh b/tests/zfs-tests/tests/functional/casenorm/mixed_create_failure.ksh
|
|
new file mode 100755
|
|
index 00000000..51b5bb3f
|
|
--- /dev/null
|
|
+++ b/tests/zfs-tests/tests/functional/casenorm/mixed_create_failure.ksh
|
|
@@ -0,0 +1,136 @@
|
|
+#!/bin/ksh -p
|
|
+#
|
|
+#
|
|
+# This file and its contents are supplied under the terms of the
|
|
+# Common Development and Distribution License ("CDDL"), version 1.0.
|
|
+# You may only use this file in accordance with the terms of version
|
|
+# 1.0 of the CDDL.
|
|
+#
|
|
+# A full copy of the text of the CDDL should have accompanied this
|
|
+# source. A copy of the CDDL is also available via the Internet at
|
|
+# http://www.illumos.org/license/CDDL.
|
|
+#
|
|
+#
|
|
+# Copyright 2018 Nutanix Inc. All rights reserved.
|
|
+#
|
|
+
|
|
+. $STF_SUITE/tests/functional/casenorm/casenorm.kshlib
|
|
+
|
|
+# DESCRIPTION:
|
|
+# For the filesystem with casesensitivity=mixed, normalization=none,
|
|
+# when multiple files with the same name (differing only in case) are created,
|
|
+# the number of files is limited to what can fit in a fatzap leaf-block.
|
|
+# And beyond that, it fails with ENOSPC.
|
|
+#
|
|
+# Ensure that the create/rename operations fail gracefully and not trigger an
|
|
+# ASSERT.
|
|
+#
|
|
+# STRATEGY:
|
|
+# Repeat the below steps for objects: files, directories, symlinks and hardlinks
|
|
+# 1. Create objects with same name but varying in case.
|
|
+# E.g. 'abcdefghijklmnop', 'Abcdefghijklmnop', 'ABcdefghijklmnop' etc.
|
|
+# The create should fail with ENOSPC.
|
|
+# 2. Create an object with name 'tmp_obj' and try to rename it to name that we
|
|
+# failed to add in step 1 above.
|
|
+# This should fail as well.
|
|
+
|
|
+verify_runnable "global"
|
|
+
|
|
+function cleanup
|
|
+{
|
|
+ destroy_testfs
|
|
+}
|
|
+
|
|
+log_onexit cleanup
|
|
+log_assert "With mixed mode: ensure create fails with ENOSPC beyond a certain limit"
|
|
+
|
|
+create_testfs "-o casesensitivity=mixed -o normalization=none"
|
|
+
|
|
+# Different object types
|
|
+obj_type=('file' 'dir' 'symlink' 'hardlink')
|
|
+
|
|
+# Commands to create different object types
|
|
+typeset -A ops
|
|
+ops['file']='touch'
|
|
+ops['dir']='mkdir'
|
|
+ops['symlink']='ln -s'
|
|
+ops['hardlink']='ln'
|
|
+
|
|
+# This function tests the following for a give object type :
|
|
+# - Create multiple objects with the same name (varying only in case).
|
|
+# Ensure that it eventually fails once the leaf-block limit is exceeded.
|
|
+# - Create another object with a different name. And attempt rename it to the
|
|
+# name (for which the create had failed in the previous step).
|
|
+# This should fail as well.
|
|
+# Args :
|
|
+# $1 - object type (file/dir/symlink/hardlink)
|
|
+# $2 - test directory
|
|
+#
|
|
+function test_ops
|
|
+{
|
|
+ typeset obj_type=$1
|
|
+ typeset testdir=$2
|
|
+
|
|
+ target_obj='target-file'
|
|
+
|
|
+ op="${ops[$obj_type]}"
|
|
+
|
|
+ log_note "The op : $op"
|
|
+ log_note "testdir=$testdir obj_type=$obj_type"
|
|
+
|
|
+ test_path="$testdir/$obj_type"
|
|
+ mkdir $test_path
|
|
+ log_note "Created test dir $test_path"
|
|
+
|
|
+ if [[ $obj_type = "symlink" || $obj_type = "hardlink" ]]; then
|
|
+ touch $test_path/$target_obj
|
|
+ log_note "Created target: $test_path/$target_obj"
|
|
+ op="$op $test_path/$target_obj"
|
|
+ fi
|
|
+
|
|
+ log_note "op : $op"
|
|
+ names='{a,A}{b,B}{c,C}{d,D}{e,E}{f,F}{g,G}{h,H}{i,I}{j,J}{k,K}{l,L}'
|
|
+ for name in $names; do
|
|
+ cmd="$op $test_path/$name"
|
|
+ out=$($cmd 2>&1)
|
|
+ ret=$?
|
|
+ log_note "cmd: $cmd ret: $ret out=$out"
|
|
+ if (($ret != 0)); then
|
|
+ if [[ $out = *@(No space left on device)* ]]; then
|
|
+ save_name="$test_path/$name"
|
|
+ break;
|
|
+ else
|
|
+ log_err "$cmd failed with unexpected error : $out"
|
|
+ fi
|
|
+ fi
|
|
+ done
|
|
+
|
|
+ log_note 'Test rename \"sample_name\" rename'
|
|
+ TMP_OBJ="$test_path/tmp_obj"
|
|
+ cmd="$op $TMP_OBJ"
|
|
+ out=$($cmd 2>&1)
|
|
+ ret=$?
|
|
+ if (($ret != 0)); then
|
|
+ log_err "cmd:$cmd failed out:$out"
|
|
+ fi
|
|
+
|
|
+ # Now, try to rename the tmp_obj to the name which we failed to add earlier.
|
|
+ # This should fail as well.
|
|
+ out=$(mv $TMP_OBJ $save_name 2>&1)
|
|
+ ret=$?
|
|
+ if (($ret != 0)); then
|
|
+ if [[ $out = *@(No space left on device)* ]]; then
|
|
+ log_note "$cmd failed as expected : $out"
|
|
+ else
|
|
+ log_err "$cmd failed with : $out"
|
|
+ fi
|
|
+ fi
|
|
+}
|
|
+
|
|
+for obj_type in ${obj_type[*]};
|
|
+do
|
|
+ log_note "Testing create of $obj_type"
|
|
+ test_ops $obj_type $TESTDIR
|
|
+done
|
|
+
|
|
+log_pass "Mixed mode FS: Ops on large number of colliding names fail gracefully"
|
|
diff --git a/tests/zfs-tests/tests/functional/cp_files/.gitignore b/tests/zfs-tests/tests/functional/cp_files/.gitignore
|
|
new file mode 100644
|
|
index 00000000..eac05e15
|
|
--- /dev/null
|
|
+++ b/tests/zfs-tests/tests/functional/cp_files/.gitignore
|
|
@@ -0,0 +1 @@
|
|
+/cp_files
|
|
diff --git a/tests/zfs-tests/tests/functional/cp_files/Makefile.am b/tests/zfs-tests/tests/functional/cp_files/Makefile.am
|
|
new file mode 100644
|
|
index 00000000..06c31f5f
|
|
--- /dev/null
|
|
+++ b/tests/zfs-tests/tests/functional/cp_files/Makefile.am
|
|
@@ -0,0 +1,13 @@
|
|
+include $(top_srcdir)/config/Rules.am
|
|
+
|
|
+pkgdatadir = $(datadir)/@PACKAGE@/zfs-tests/tests/functional/cp_files
|
|
+
|
|
+dist_pkgdata_SCRIPTS = \
|
|
+ cp_files_001_pos.ksh \
|
|
+ cleanup.ksh \
|
|
+ setup.ksh
|
|
+
|
|
+pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/tests/functional/cp_files
|
|
+
|
|
+pkgexec_PROGRAMS = cp_files
|
|
+cp_files_SOURCES= cp_files.c
|
|
diff --git a/tests/zfs-tests/tests/functional/cp_files/cleanup.ksh b/tests/zfs-tests/tests/functional/cp_files/cleanup.ksh
|
|
new file mode 100755
|
|
index 00000000..3166bd6e
|
|
--- /dev/null
|
|
+++ b/tests/zfs-tests/tests/functional/cp_files/cleanup.ksh
|
|
@@ -0,0 +1,34 @@
|
|
+#!/bin/ksh -p
|
|
+#
|
|
+# CDDL HEADER START
|
|
+#
|
|
+# The contents of this file are subject to the terms of the
|
|
+# Common Development and Distribution License (the "License").
|
|
+# You may not use this file except in compliance with the License.
|
|
+#
|
|
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
|
+# or http://www.opensolaris.org/os/licensing.
|
|
+# See the License for the specific language governing permissions
|
|
+# and limitations under the License.
|
|
+#
|
|
+# When distributing Covered Code, include this CDDL HEADER in each
|
|
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
|
+# If applicable, add the following below this CDDL HEADER, with the
|
|
+# fields enclosed by brackets "[]" replaced with your own identifying
|
|
+# information: Portions Copyright [yyyy] [name of copyright owner]
|
|
+#
|
|
+# CDDL HEADER END
|
|
+#
|
|
+
|
|
+#
|
|
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
|
|
+# Use is subject to license terms.
|
|
+#
|
|
+
|
|
+#
|
|
+# Copyright (c) 2013 by Delphix. All rights reserved.
|
|
+#
|
|
+
|
|
+. $STF_SUITE/include/libtest.shlib
|
|
+
|
|
+default_cleanup
|
|
diff --git a/tests/zfs-tests/tests/functional/cp_files/cp_files.c b/tests/zfs-tests/tests/functional/cp_files/cp_files.c
|
|
new file mode 100644
|
|
index 00000000..9af64a11
|
|
--- /dev/null
|
|
+++ b/tests/zfs-tests/tests/functional/cp_files/cp_files.c
|
|
@@ -0,0 +1,58 @@
|
|
+#include <stdio.h>
|
|
+#include <stdlib.h>
|
|
+#include <unistd.h>
|
|
+#include <sys/types.h>
|
|
+#include <sys/stat.h>
|
|
+#include <fcntl.h>
|
|
+#include <dirent.h>
|
|
+#include <errno.h>
|
|
+#include <string.h>
|
|
+
|
|
+int
|
|
+main(int argc, char *argv[])
|
|
+{
|
|
+ int tfd;
|
|
+ DIR *sdir;
|
|
+ struct dirent *dirent;
|
|
+
|
|
+ if (argc != 3) {
|
|
+ fprintf(stderr, "Usage: %s SRC DST\n", argv[0]);
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ sdir = opendir(argv[1]);
|
|
+ if (sdir == NULL) {
|
|
+ fprintf(stderr, "Failed to open %s: %s\n",
|
|
+ argv[1], strerror(errno));
|
|
+ exit(2);
|
|
+ }
|
|
+
|
|
+ tfd = open(argv[2], O_DIRECTORY);
|
|
+ if (tfd < 0) {
|
|
+ fprintf(stderr, "Failed to open %s: %s\n",
|
|
+ argv[2], strerror(errno));
|
|
+ closedir(sdir);
|
|
+ exit(3);
|
|
+ }
|
|
+
|
|
+ while ((dirent = readdir(sdir)) != NULL) {
|
|
+ if (dirent->d_name[0] == '.' &&
|
|
+ (dirent->d_name[1] == '.' || dirent->d_name[1] == '\0'))
|
|
+ continue;
|
|
+
|
|
+ int fd = openat(tfd, dirent->d_name, O_CREAT|O_WRONLY, 0666);
|
|
+ if (fd < 0) {
|
|
+ fprintf(stderr, "Failed to create %s/%s: %s\n",
|
|
+ argv[2], dirent->d_name, strerror(errno));
|
|
+ closedir(sdir);
|
|
+ close(tfd);
|
|
+ exit(4);
|
|
+ }
|
|
+ close(fd);
|
|
+ }
|
|
+
|
|
+ closedir(sdir);
|
|
+ close(tfd);
|
|
+
|
|
+ return (0);
|
|
+}
|
|
diff --git a/tests/zfs-tests/tests/functional/cp_files/cp_files_001_pos.ksh b/tests/zfs-tests/tests/functional/cp_files/cp_files_001_pos.ksh
|
|
new file mode 100755
|
|
index 00000000..3e138cfc
|
|
--- /dev/null
|
|
+++ b/tests/zfs-tests/tests/functional/cp_files/cp_files_001_pos.ksh
|
|
@@ -0,0 +1,74 @@
|
|
+#! /bin/ksh -p
|
|
+#
|
|
+# CDDL HEADER START
|
|
+#
|
|
+# The contents of this file are subject to the terms of the
|
|
+# Common Development and Distribution License (the "License").
|
|
+# You may not use this file except in compliance with the License.
|
|
+#
|
|
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
|
+# or http://www.opensolaris.org/os/licensing.
|
|
+# See the License for the specific language governing permissions
|
|
+# and limitations under the License.
|
|
+#
|
|
+# When distributing Covered Code, include this CDDL HEADER in each
|
|
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
|
+# If applicable, add the following below this CDDL HEADER, with the
|
|
+# fields enclosed by brackets "[]" replaced with your own identifying
|
|
+# information: Portions Copyright [yyyy] [name of copyright owner]
|
|
+#
|
|
+# CDDL HEADER END
|
|
+#
|
|
+
|
|
+#
|
|
+# Copyright (c) 2018 by Nutanix. All rights reserved.
|
|
+#
|
|
+
|
|
+. $STF_SUITE/include/libtest.shlib
|
|
+
|
|
+#
|
|
+# DESCRIPTION:
|
|
+# Copy a large number of files between 2 directories
|
|
+# within a zfs filesystem works without errors.
|
|
+# This make sure zap upgrading and expanding works.
|
|
+#
|
|
+# STRATEGY:
|
|
+#
|
|
+# 1. Create NR_FILES files in directory src
|
|
+# 2. Check the number of files is correct
|
|
+# 3. Copy files from src to dst in readdir order
|
|
+# 4. Check the number of files is correct
|
|
+#
|
|
+
|
|
+verify_runnable "global"
|
|
+
|
|
+function cleanup
|
|
+{
|
|
+ rm -rf $TESTDIR/src $TESTDIR/dst
|
|
+}
|
|
+
|
|
+log_assert "Copy a large number of files between 2 directories" \
|
|
+ "within a zfs filesystem works without errors"
|
|
+
|
|
+log_onexit cleanup
|
|
+
|
|
+NR_FILES=60000
|
|
+BATCH=1000
|
|
+
|
|
+log_must mkdir $TESTDIR/src
|
|
+log_must mkdir $TESTDIR/dst
|
|
+
|
|
+WD=$(pwd)
|
|
+cd $TESTDIR/src
|
|
+# create NR_FILES in BATCH at a time to prevent overflowing argument buffer
|
|
+for i in $(seq $(($NR_FILES/$BATCH))); do touch $(seq $((($i-1)*$BATCH+1)) $(($i*$BATCH))); done
|
|
+cd $WD
|
|
+
|
|
+log_must test $NR_FILES -eq $(ls -U $TESTDIR/src | wc -l)
|
|
+
|
|
+# copy files from src to dst, use cp_files to make sure we copy in readdir order
|
|
+log_must $STF_SUITE/tests/functional/cp_files/cp_files $TESTDIR/src $TESTDIR/dst
|
|
+
|
|
+log_must test $NR_FILES -eq $(ls -U $TESTDIR/dst | wc -l)
|
|
+
|
|
+log_pass
|
|
diff --git a/tests/zfs-tests/tests/functional/cp_files/setup.ksh b/tests/zfs-tests/tests/functional/cp_files/setup.ksh
|
|
new file mode 100755
|
|
index 00000000..fc5cec30
|
|
--- /dev/null
|
|
+++ b/tests/zfs-tests/tests/functional/cp_files/setup.ksh
|
|
@@ -0,0 +1,35 @@
|
|
+#!/bin/ksh -p
|
|
+#
|
|
+# CDDL HEADER START
|
|
+#
|
|
+# The contents of this file are subject to the terms of the
|
|
+# Common Development and Distribution License (the "License").
|
|
+# You may not use this file except in compliance with the License.
|
|
+#
|
|
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
|
+# or http://www.opensolaris.org/os/licensing.
|
|
+# See the License for the specific language governing permissions
|
|
+# and limitations under the License.
|
|
+#
|
|
+# When distributing Covered Code, include this CDDL HEADER in each
|
|
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
|
+# If applicable, add the following below this CDDL HEADER, with the
|
|
+# fields enclosed by brackets "[]" replaced with your own identifying
|
|
+# information: Portions Copyright [yyyy] [name of copyright owner]
|
|
+#
|
|
+# CDDL HEADER END
|
|
+#
|
|
+
|
|
+#
|
|
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
|
|
+# Use is subject to license terms.
|
|
+#
|
|
+
|
|
+#
|
|
+# Copyright (c) 2013 by Delphix. All rights reserved.
|
|
+#
|
|
+
|
|
+. $STF_SUITE/include/libtest.shlib
|
|
+
|
|
+DISK=${DISKS%% *}
|
|
+default_setup $DISK
|