diff options
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | include/sys/zap_leaf.h | 15 | ||||
-rw-r--r-- | module/zfs/zap.c | 10 | ||||
-rw-r--r-- | module/zfs/zap_leaf.c | 2 | ||||
-rw-r--r-- | module/zfs/zap_micro.c | 47 | ||||
-rw-r--r-- | module/zfs/zfs_dir.c | 29 | ||||
-rw-r--r-- | module/zfs/zfs_vnops.c | 74 | ||||
-rw-r--r-- | tests/runfiles/linux.run | 6 | ||||
-rw-r--r-- | tests/zfs-tests/tests/functional/Makefile.am | 1 | ||||
-rw-r--r-- | tests/zfs-tests/tests/functional/casenorm/Makefile.am | 1 | ||||
-rwxr-xr-x | tests/zfs-tests/tests/functional/casenorm/mixed_create_failure.ksh | 136 | ||||
-rw-r--r-- | tests/zfs-tests/tests/functional/cp_files/.gitignore | 1 | ||||
-rw-r--r-- | tests/zfs-tests/tests/functional/cp_files/Makefile.am | 13 | ||||
-rwxr-xr-x | tests/zfs-tests/tests/functional/cp_files/cleanup.ksh | 34 | ||||
-rw-r--r-- | tests/zfs-tests/tests/functional/cp_files/cp_files.c | 58 | ||||
-rwxr-xr-x | tests/zfs-tests/tests/functional/cp_files/cp_files_001_pos.ksh | 74 | ||||
-rwxr-xr-x | tests/zfs-tests/tests/functional/cp_files/setup.ksh | 35 |
17 files changed, 500 insertions, 37 deletions
diff --git a/configure.ac b/configure.ac index 1715b9d0e..ebce007a6 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/include/sys/zap_leaf.h b/include/sys/zap_leaf.h index e784c5963..a3da1036a 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 2f6aed667..d899ccdb7 100644 --- a/module/zfs/zap.c +++ b/module/zfs/zap.c @@ -852,8 +852,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 5341fc098..661fd747b 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 b81a48a0f..791cbee88 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) { @@ -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, diff --git a/module/zfs/zfs_dir.c b/module/zfs/zfs_dir.c index 76f79cc11..7eb426b78 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 f7f2f6167..0eeb2bb42 100644 --- a/module/zfs/zfs_vnops.c +++ b/module/zfs/zfs_vnops.c @@ -1450,6 +1450,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) { @@ -1467,10 +1468,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; @@ -2064,13 +2077,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); @@ -2080,6 +2098,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); @@ -2089,10 +2108,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); } /* @@ -3950,6 +3973,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); } } @@ -4120,14 +4150,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); @@ -4135,10 +4169,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 7b51ef47b..7e4a5a7c3 100644 --- a/tests/runfiles/linux.run +++ b/tests/runfiles/linux.run @@ -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'] diff --git a/tests/zfs-tests/tests/functional/Makefile.am b/tests/zfs-tests/tests/functional/Makefile.am index a5bbb36e0..6388f2d0f 100644 --- a/tests/zfs-tests/tests/functional/Makefile.am +++ b/tests/zfs-tests/tests/functional/Makefile.am @@ -13,6 +13,7 @@ SUBDIRS = \ cli_root \ cli_user \ compression \ + cp_files \ ctime \ deadman \ delegate \ diff --git a/tests/zfs-tests/tests/functional/casenorm/Makefile.am b/tests/zfs-tests/tests/functional/casenorm/Makefile.am index 65dd156e7..b284a2560 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 000000000..51b5bb3f6 --- /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 000000000..eac05e155 --- /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 000000000..06c31f5f3 --- /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 000000000..3166bd6ec --- /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 000000000..9af64a112 --- /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 000000000..3e138cfc9 --- /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 000000000..fc5cec306 --- /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 |