Fix ENOSPC in "Handle zap_add() failures in ..."
Commitcc63068
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. Sincecc63068
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
This commit is contained in:
parent
b0ee5946aa
commit
599b864813
@ -253,6 +253,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/deadman/Makefile
|
||||
tests/zfs-tests/tests/functional/delegate/Makefile
|
||||
|
@ -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
|
||||
|
@ -852,8 +852,16 @@ fzap_add_cd(zap_name_t *zn,
|
||||
} 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:
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
@ -638,16 +673,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);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1190,7 +1224,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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -1450,6 +1450,7 @@ zfs_create(struct inode *dip, char *name, vattr_t *vap, int excl,
|
||||
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) {
|
||||
@ -1467,10 +1468,22 @@ zfs_create(struct inode *dip, char *name, vattr_t *vap, int excl,
|
||||
}
|
||||
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;
|
||||
@ -2064,13 +2077,18 @@ zfs_mkdir(struct inode *dip, char *dirname, vattr_t *vap, struct inode **ipp,
|
||||
*/
|
||||
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);
|
||||
|
||||
@ -2080,6 +2098,7 @@ zfs_mkdir(struct inode *dip, char *dirname, vattr_t *vap, struct inode **ipp,
|
||||
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);
|
||||
@ -2089,10 +2108,14 @@ zfs_mkdir(struct inode *dip, char *dirname, vattr_t *vap, struct inode **ipp,
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -3950,6 +3973,13 @@ zfs_rename(struct inode *sdip, char *snm, struct inode *tdip, char *tnm,
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4120,14 +4150,18 @@ zfs_symlink(struct inode *dip, char *name, vattr_t *vap, char *link,
|
||||
/*
|
||||
* Insert the new object into the directory.
|
||||
*/
|
||||
(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));
|
||||
} else {
|
||||
if (flags & FIGNORECASE)
|
||||
txtype |= TX_CI;
|
||||
zfs_log_symlink(zilog, tx, txtype, dzp, zp, name, link);
|
||||
|
||||
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);
|
||||
|
||||
@ -4135,10 +4169,14 @@ zfs_symlink(struct inode *dip, char *name, vattr_t *vap, char *link,
|
||||
|
||||
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);
|
||||
|
@ -59,7 +59,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/channel_program/lua_core]
|
||||
@ -475,6 +475,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']
|
||||
|
@ -13,6 +13,7 @@ SUBDIRS = \
|
||||
cli_root \
|
||||
cli_user \
|
||||
compression \
|
||||
cp_files \
|
||||
ctime \
|
||||
deadman \
|
||||
delegate \
|
||||
|
@ -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 \
|
||||
|
136
tests/zfs-tests/tests/functional/casenorm/mixed_create_failure.ksh
Executable file
136
tests/zfs-tests/tests/functional/casenorm/mixed_create_failure.ksh
Executable file
@ -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"
|
1
tests/zfs-tests/tests/functional/cp_files/.gitignore
vendored
Normal file
1
tests/zfs-tests/tests/functional/cp_files/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/cp_files
|
13
tests/zfs-tests/tests/functional/cp_files/Makefile.am
Normal file
13
tests/zfs-tests/tests/functional/cp_files/Makefile.am
Normal file
@ -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
|
34
tests/zfs-tests/tests/functional/cp_files/cleanup.ksh
Executable file
34
tests/zfs-tests/tests/functional/cp_files/cleanup.ksh
Executable file
@ -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
|
58
tests/zfs-tests/tests/functional/cp_files/cp_files.c
Normal file
58
tests/zfs-tests/tests/functional/cp_files/cp_files.c
Normal file
@ -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);
|
||||
}
|
74
tests/zfs-tests/tests/functional/cp_files/cp_files_001_pos.ksh
Executable file
74
tests/zfs-tests/tests/functional/cp_files/cp_files_001_pos.ksh
Executable file
@ -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
|
35
tests/zfs-tests/tests/functional/cp_files/setup.ksh
Executable file
35
tests/zfs-tests/tests/functional/cp_files/setup.ksh
Executable file
@ -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
|
Loading…
Reference in New Issue
Block a user