From 9f7f4da0ebae6983feddb84961182ab5c0d3b74d Mon Sep 17 00:00:00 2001 From: Bob Glossman Date: Thu, 20 Jun 2013 13:08:31 +0800 Subject: [PATCH] LU-2901 mdt: duplicate link names in directory When creating a hard link to a file, the MDT/MDD/OSD code does not verify whether the target link name already exists in the directory. The ZFS ZAP code checks for duplicate entries. The add_dirent_to_buf() function in ldiskfs only checks entries for duplicates while it is traversing the leaf block looking for free space. Even if it scanned the whole leaf block, this would not work for non-htree directories since there is no guarantee that the name is being inserted into the same leaf block. To fix this, link should check target object doesn't exist as other creat operations. Add sanity.sh test_31o with multiple threads racing to link a new name into the directory, while ensuring that there is a free entry in the leaf block that is large enough to hold the duplicate name. This needs to be racy, because otherwise the client VFS will see the existing name and not send the RPC to the MDS, hiding the bug. Lustre-change: http://review.whamcloud.com/6591 Signed-off-by: Andreas Dilger Signed-off-by: Lai Siyao Signed-off-by: Bob Glossman Signed-off-by: Jian Yu Change-Id: Idb7976224b4860a1a07dac4d4467d67a93cda84f Reviewed-on: http://review.whamcloud.com/6678 Tested-by: Hudson Tested-by: Maloo Reviewed-by: Oleg Drokin --- lustre/mdt/mdt_reint.c | 5 +++++ lustre/tests/mlink.c | 22 +++++++++++----------- lustre/tests/sanity.sh | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 11 deletions(-) diff --git a/lustre/mdt/mdt_reint.c b/lustre/mdt/mdt_reint.c index 455ea85..cfd693d 100644 --- a/lustre/mdt/mdt_reint.c +++ b/lustre/mdt/mdt_reint.c @@ -879,6 +879,11 @@ static int mdt_reint_link(struct mdt_thread_info *info, GOTO(out_unlock_child, rc); /* save version of file name for replay, it must be ENOENT here */ if (!req_is_replay(mdt_info_req(info))) { + if (rc != -ENOENT) { + CDEBUG(D_INFO, "link target %.*s existed!\n", + rr->rr_namelen, (char *)rr->rr_name); + GOTO(out_unlock_child, rc = -EEXIST); + } info->mti_ver[2] = ENOENT_VERSION; mdt_version_save(mdt_info_req(info), info->mti_ver[2], 2); } diff --git a/lustre/tests/mlink.c b/lustre/tests/mlink.c index ed34591..859201b 100755 --- a/lustre/tests/mlink.c +++ b/lustre/tests/mlink.c @@ -44,18 +44,18 @@ int main(int argc, char ** argv) { - int rc; + int rc; - if (argc < 3) { - printf("Usage: %s file link\n", argv[0]); - return 1; - } + if (argc < 3) { + fprintf(stderr, "usage: %s file link\n", argv[0]); + return 1; + } - rc = link(argv[1], argv[2]); - if (rc) { - printf("link(%s, %s) error: %s\n", argv[1], argv[2], - strerror(errno)); + rc = link(argv[1], argv[2]); + if (rc) { + fprintf(stderr, "link(%s, %s) error: %s\n", + argv[1], argv[2], strerror(errno)); return errno; - } + } return 0; -} +} diff --git a/lustre/tests/sanity.sh b/lustre/tests/sanity.sh index c5e583d..9537246 100644 --- a/lustre/tests/sanity.sh +++ b/lustre/tests/sanity.sh @@ -1736,6 +1736,38 @@ test_31m() { } run_test 31m "link to file: the same, non-existing, dir===============" +link_one() { + local TEMPNAME=$(mktemp $1_XXXXXX) + mlink $TEMPNAME $1 2> /dev/null && + echo "$BASHPID: link $TEMPNAME to $1 succeeded" + munlink $TEMPNAME +} + +test_31o() { # LU-2901 + local server_version=$(lustre_version_code $SINGLEMDS) + + if [[ $server_version -lt $(version_code 2.1.6) ]] || + [[ $server_version -ge $(version_code 2.2.0) && + $server_version -lt $(version_code 2.4.1) ]]; then + skip "Need MDS version at least 2.1.6 or 2.4.1" + return 0 + fi + + mkdir -p $DIR/$tdir + for LOOP in $(seq 100); do + rm -f $DIR/$tdir/$tfile* + for THREAD in $(seq 8); do + link_one $DIR/$tdir/$tfile.$LOOP & + done + wait + local LINKS=$(ls -1 $DIR/$tdir | grep -c $tfile.$LOOP) + [ $LINKS -gt 1 ] && ls $DIR/$tdir && + error "$LINKS duplicate links to $tfile.$LOOP" && + break || true + done +} +run_test 31o "duplicate hard links with same filename" + test_32a() { echo "== more mountpoints and symlinks =================" [ -e $DIR/d32a ] && rm -fr $DIR/d32a -- 1.8.3.1