From 9b7b9cd370ed2ec4c8ee346bcb95cb30ba7e9ba9 Mon Sep 17 00:00:00 2001 From: George Melikov Date: Fri, 3 Feb 2017 01:13:41 +0300 Subject: [PATCH] OpenZFS 1300 - filename normalization doesn't work for removes Authored by: Kevin Crowe Reviewed by: Yuri Pankov Reviewed by: Pavel Zakharov Reviewed by: Matt Ahrens Reviewed-by: Brian Behlendorf Ported-by: George Melikov OpenZFS-issue: https://www.illumos.org/issues/1300 OpenZFS-commit: https://github.com/openzfs/openzfs/commit/8f1750d Closes #5725 Porting notes: - zap_micro.c: all `MT_EXACT` are replaced by `0` --- include/sys/zap.h | 38 ++++----------- include/sys/zap_impl.h | 3 ++ module/zfs/dmu_objset.c | 5 +- module/zfs/dsl_bookmark.c | 14 +++--- module/zfs/dsl_dataset.c | 18 ++++--- module/zfs/zap_leaf.c | 20 +++----- module/zfs/zap_micro.c | 99 ++++++++++++++++++++++++++++----------- module/zfs/zfs_dir.c | 88 ++++++++++++++++++++++++---------- module/zfs/zfs_vnops.c | 17 +++++-- 9 files changed, 185 insertions(+), 117 deletions(-) diff --git a/include/sys/zap.h b/include/sys/zap.h index 513c8f672a36..43b7fbd263c2 100644 --- a/include/sys/zap.h +++ b/include/sys/zap.h @@ -18,9 +18,11 @@ * * CDDL HEADER END */ + /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013 by Delphix. All rights reserved. + * Copyright 2017 Nexenta Systems, Inc. */ #ifndef _SYS_ZAP_H @@ -87,22 +89,15 @@ extern "C" { /* * Specifies matching criteria for ZAP lookups. + * MT_NORMALIZE Use ZAP normalization flags, which can include both + * unicode normalization and case-insensitivity. + * MT_MATCH_CASE Do case-sensitive lookups even if MT_NORMALIZE is + * specified and ZAP normalization flags include + * U8_TEXTPREP_TOUPPER. */ -typedef enum matchtype -{ - /* Only find an exact match (non-normalized) */ - MT_EXACT, - /* - * If there is an exact match, find that, otherwise find the - * first normalized match. - */ - MT_BEST, - /* - * Find the "first" normalized (case and Unicode form) match; - * the designated "first" match will not change as long as the - * set of entries with this normalization doesn't change. - */ - MT_FIRST +typedef enum matchtype { + MT_NORMALIZE = 1 << 0, + MT_MATCH_CASE = 1 << 1, } matchtype_t; typedef enum zap_flags { @@ -119,19 +114,6 @@ typedef enum zap_flags { /* * Create a new zapobj with no attributes and return its object number. - * MT_EXACT will cause the zap object to only support MT_EXACT lookups, - * otherwise any matchtype can be used for lookups. - * - * normflags specifies what normalization will be done. values are: - * 0: no normalization (legacy on-disk format, supports MT_EXACT matching - * only) - * U8_TEXTPREP_TOLOWER: case normalization will be performed. - * MT_FIRST/MT_BEST matching will find entries that match without - * regard to case (eg. looking for "foo" can find an entry "Foo"). - * Eventually, other flags will permit unicode normalization as well. - * - * dnodesize specifies the on-disk size of the dnode for the new zapobj. - * Valid values are multiples of 512 up to DNODE_MAX_SIZE. */ uint64_t zap_create(objset_t *ds, dmu_object_type_t ot, dmu_object_type_t bonustype, int bonuslen, dmu_tx_t *tx); diff --git a/include/sys/zap_impl.h b/include/sys/zap_impl.h index cbe7f3c5b9d4..fb0f1a012704 100644 --- a/include/sys/zap_impl.h +++ b/include/sys/zap_impl.h @@ -18,10 +18,12 @@ * * CDDL HEADER END */ + /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2014 Spectra Logic Corporation, All rights reserved. * Copyright (c) 2013, 2016 by Delphix. All rights reserved. + * Copyright 2017 Nexenta Systems, Inc. */ #ifndef _SYS_ZAP_IMPL_H @@ -188,6 +190,7 @@ typedef struct zap_name { int zn_key_norm_numints; uint64_t zn_hash; matchtype_t zn_matchtype; + int zn_normflags; char zn_normbuf[ZAP_MAXNAMELEN]; } zap_name_t; diff --git a/module/zfs/dmu_objset.c b/module/zfs/dmu_objset.c index f09cd9e3c0fc..53b8a759a0dd 100644 --- a/module/zfs/dmu_objset.c +++ b/module/zfs/dmu_objset.c @@ -18,15 +18,16 @@ * * CDDL HEADER END */ + /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2016 by Delphix. All rights reserved. * Copyright (c) 2013 by Saso Kiselkov. All rights reserved. * Copyright (c) 2013, Joyent, Inc. All rights reserved. * Copyright (c) 2014 Spectra Logic Corporation, All rights reserved. - * Copyright (c) 2015 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2015, STRATO AG, Inc. All rights reserved. * Copyright (c) 2016 Actifio, Inc. All rights reserved. + * Copyright 2017 Nexenta Systems, Inc. */ /* Portions Copyright 2010 Robert Milkowski */ @@ -1849,7 +1850,7 @@ dmu_snapshot_realname(objset_t *os, char *name, char *real, int maxlen, return (zap_lookup_norm(ds->ds_dir->dd_pool->dp_meta_objset, dsl_dataset_phys(ds)->ds_snapnames_zapobj, name, 8, 1, &ignored, - MT_FIRST, real, maxlen, conflict)); + MT_NORMALIZE, real, maxlen, conflict)); } int diff --git a/module/zfs/dsl_bookmark.c b/module/zfs/dsl_bookmark.c index 5a7f034ce649..64f5788c4042 100644 --- a/module/zfs/dsl_bookmark.c +++ b/module/zfs/dsl_bookmark.c @@ -12,8 +12,10 @@ * * CDDL HEADER END */ + /* * Copyright (c) 2013, 2014 by Delphix. All rights reserved. + * Copyright 2017 Nexenta Systems, Inc. */ #include @@ -59,16 +61,14 @@ dsl_dataset_bmark_lookup(dsl_dataset_t *ds, const char *shortname, { objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; uint64_t bmark_zapobj = ds->ds_bookmarks; - matchtype_t mt; + matchtype_t mt = 0; int err; if (bmark_zapobj == 0) return (SET_ERROR(ESRCH)); if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET) - mt = MT_FIRST; - else - mt = MT_EXACT; + mt = MT_NORMALIZE; err = zap_lookup_norm(mos, bmark_zapobj, shortname, sizeof (uint64_t), sizeof (*bmark_phys) / sizeof (uint64_t), bmark_phys, mt, @@ -342,12 +342,10 @@ dsl_dataset_bookmark_remove(dsl_dataset_t *ds, const char *name, dmu_tx_t *tx) { objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; uint64_t bmark_zapobj = ds->ds_bookmarks; - matchtype_t mt; + matchtype_t mt = 0; if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET) - mt = MT_FIRST; - else - mt = MT_EXACT; + mt = MT_NORMALIZE; return (zap_remove_norm(mos, bmark_zapobj, name, mt, tx)); } diff --git a/module/zfs/dsl_dataset.c b/module/zfs/dsl_dataset.c index b6afb33a48b2..0665c12fc0c2 100644 --- a/module/zfs/dsl_dataset.c +++ b/module/zfs/dsl_dataset.c @@ -18,6 +18,7 @@ * * CDDL HEADER END */ + /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2011, 2016 by Delphix. All rights reserved. @@ -26,6 +27,7 @@ * Copyright (c) 2014 Spectra Logic Corporation, All rights reserved. * Copyright (c) 2016 Actifio, Inc. All rights reserved. * Copyright 2016, OmniTI Computer Consulting, Inc. All rights reserved. + * Copyright 2017 Nexenta Systems, Inc. */ #include @@ -360,17 +362,15 @@ dsl_dataset_snap_lookup(dsl_dataset_t *ds, const char *name, uint64_t *value) { objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; uint64_t snapobj = dsl_dataset_phys(ds)->ds_snapnames_zapobj; - matchtype_t mt; + matchtype_t mt = 0; int err; if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET) - mt = MT_FIRST; - else - mt = MT_EXACT; + mt = MT_NORMALIZE; err = zap_lookup_norm(mos, snapobj, name, 8, 1, value, mt, NULL, 0, NULL); - if (err == ENOTSUP && mt == MT_FIRST) + if (err == ENOTSUP && (mt & MT_NORMALIZE)) err = zap_lookup(mos, snapobj, name, 8, 1, value); return (err); } @@ -381,18 +381,16 @@ dsl_dataset_snap_remove(dsl_dataset_t *ds, const char *name, dmu_tx_t *tx, { objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; uint64_t snapobj = dsl_dataset_phys(ds)->ds_snapnames_zapobj; - matchtype_t mt; + matchtype_t mt = 0; int err; dsl_dir_snap_cmtime_update(ds->ds_dir); if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET) - mt = MT_FIRST; - else - mt = MT_EXACT; + mt = MT_NORMALIZE; err = zap_remove_norm(mos, snapobj, name, mt, tx); - if (err == ENOTSUP && mt == MT_FIRST) + if (err == ENOTSUP && (mt & MT_NORMALIZE)) err = zap_remove(mos, snapobj, name, tx); if (err == 0 && adj_cnt) diff --git a/module/zfs/zap_leaf.c b/module/zfs/zap_leaf.c index c9837981d89e..c342695c7f42 100644 --- a/module/zfs/zap_leaf.c +++ b/module/zfs/zap_leaf.c @@ -18,9 +18,11 @@ * * CDDL HEADER END */ + /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013, 2015 by Delphix. All rights reserved. + * Copyright 2017 Nexenta Systems, Inc. */ /* @@ -364,7 +366,7 @@ zap_leaf_array_match(zap_leaf_t *l, zap_name_t *zn, } ASSERT(zn->zn_key_intlen == 1); - if (zn->zn_matchtype == MT_FIRST) { + if (zn->zn_matchtype & MT_NORMALIZE) { char *thisname = kmem_alloc(array_numints, KM_SLEEP); boolean_t match; @@ -406,7 +408,6 @@ zap_leaf_lookup(zap_leaf_t *l, zap_name_t *zn, zap_entry_handle_t *zeh) ASSERT3U(zap_leaf_phys(l)->l_hdr.lh_magic, ==, ZAP_LEAF_MAGIC); -again: for (chunkp = LEAF_HASH_ENTPTR(l, zn->zn_hash); *chunkp != CHAIN_END; chunkp = &le->le_next) { uint16_t chunk = *chunkp; @@ -421,9 +422,9 @@ zap_leaf_lookup(zap_leaf_t *l, zap_name_t *zn, zap_entry_handle_t *zeh) /* * NB: the entry chain is always sorted by cd on * normalized zap objects, so this will find the - * lowest-cd match for MT_FIRST. + * lowest-cd match for MT_NORMALIZE. */ - ASSERT(zn->zn_matchtype == MT_EXACT || + ASSERT((zn->zn_matchtype == 0) || (zap_leaf_phys(l)->l_hdr.lh_flags & ZLF_ENTRIES_CDSORTED)); if (zap_leaf_array_match(l, zn, le->le_name_chunk, le->le_name_numints)) { @@ -437,15 +438,6 @@ zap_leaf_lookup(zap_leaf_t *l, zap_name_t *zn, zap_entry_handle_t *zeh) } } - /* - * NB: we could of course do this in one pass, but that would be - * a pain. We'll see if MT_BEST is even used much. - */ - if (zn->zn_matchtype == MT_BEST) { - zn->zn_matchtype = MT_FIRST; - goto again; - } - return (SET_ERROR(ENOENT)); } @@ -700,7 +692,7 @@ zap_entry_normalization_conflict(zap_entry_handle_t *zeh, zap_name_t *zn, continue; if (zn == NULL) { - zn = zap_name_alloc(zap, name, MT_FIRST); + zn = zap_name_alloc(zap, name, MT_NORMALIZE); allocdzn = B_TRUE; } if (zap_leaf_array_match(zeh->zeh_leaf, zn, diff --git a/module/zfs/zap_micro.c b/module/zfs/zap_micro.c index cf552ed10ec0..0c8ee9d25a7e 100644 --- a/module/zfs/zap_micro.c +++ b/module/zfs/zap_micro.c @@ -18,10 +18,12 @@ * * CDDL HEADER END */ + /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2011, 2016 by Delphix. All rights reserved. * Copyright (c) 2014 Spectra Logic Corporation, All rights reserved. + * Copyright 2017 Nexenta Systems, Inc. */ #include @@ -132,7 +134,7 @@ zap_hash(zap_name_t *zn) } static int -zap_normalize(zap_t *zap, const char *name, char *namenorm) +zap_normalize(zap_t *zap, const char *name, char *namenorm, int normflags) { size_t inlen, outlen; int err; @@ -144,8 +146,8 @@ zap_normalize(zap_t *zap, const char *name, char *namenorm) err = 0; (void) u8_textprep_str((char *)name, &inlen, namenorm, &outlen, - zap->zap_normflags | U8_TEXTPREP_IGNORE_NULL | - U8_TEXTPREP_IGNORE_INVALID, U8_UNICODE_LATEST, &err); + normflags | U8_TEXTPREP_IGNORE_NULL | U8_TEXTPREP_IGNORE_INVALID, + U8_UNICODE_LATEST, &err); return (err); } @@ -155,15 +157,15 @@ zap_match(zap_name_t *zn, const char *matchname) { ASSERT(!(zap_getflags(zn->zn_zap) & ZAP_FLAG_UINT64_KEY)); - if (zn->zn_matchtype == MT_FIRST) { + if (zn->zn_matchtype & MT_NORMALIZE) { char norm[ZAP_MAXNAMELEN]; - if (zap_normalize(zn->zn_zap, matchname, norm) != 0) + if (zap_normalize(zn->zn_zap, matchname, norm, + zn->zn_normflags) != 0) return (B_FALSE); return (strcmp(zn->zn_key_norm, norm) == 0); } else { - /* MT_BEST or MT_EXACT */ return (strcmp(zn->zn_key_orig, matchname) == 0); } } @@ -184,15 +186,30 @@ zap_name_alloc(zap_t *zap, const char *key, matchtype_t mt) zn->zn_key_orig = key; zn->zn_key_orig_numints = strlen(zn->zn_key_orig) + 1; zn->zn_matchtype = mt; + zn->zn_normflags = zap->zap_normflags; + + /* + * If we're dealing with a case sensitive lookup on a mixed or + * insensitive fs, remove U8_TEXTPREP_TOUPPER or the lookup + * will fold case to all caps overriding the lookup request. + */ + if (mt & MT_MATCH_CASE) + zn->zn_normflags &= ~U8_TEXTPREP_TOUPPER; + if (zap->zap_normflags) { - if (zap_normalize(zap, key, zn->zn_normbuf) != 0) { + /* + * We *must* use zap_normflags because this normalization is + * what the hash is computed from. + */ + if (zap_normalize(zap, key, zn->zn_normbuf, + zap->zap_normflags) != 0) { zap_name_free(zn); return (NULL); } zn->zn_key_norm = zn->zn_normbuf; zn->zn_key_norm_numints = strlen(zn->zn_key_norm) + 1; } else { - if (mt != MT_EXACT) { + if (mt != 0) { zap_name_free(zn); return (NULL); } @@ -201,6 +218,20 @@ zap_name_alloc(zap_t *zap, const char *key, matchtype_t mt) } zn->zn_hash = zap_hash(zn); + + if (zap->zap_normflags != zn->zn_normflags) { + /* + * We *must* use zn_normflags because this normalization is + * what the matching is based on. (Not the hash!) + */ + if (zap_normalize(zap, key, zn->zn_normbuf, + zn->zn_normflags) != 0) { + zap_name_free(zn); + return (NULL); + } + zn->zn_key_norm_numints = strlen(zn->zn_key_norm) + 1; + } + return (zn); } @@ -214,7 +245,7 @@ zap_name_alloc_uint64(zap_t *zap, const uint64_t *key, int numints) zn->zn_key_intlen = sizeof (*key); zn->zn_key_orig = zn->zn_key_norm = key; zn->zn_key_orig_numints = zn->zn_key_norm_numints = numints; - zn->zn_matchtype = MT_EXACT; + zn->zn_matchtype = 0; zn->zn_hash = zap_hash(zn); return (zn); @@ -294,7 +325,6 @@ mze_find(zap_name_t *zn) mze_tofind.mze_hash = zn->zn_hash; mze_tofind.mze_cd = 0; -again: mze = avl_find(avl, &mze_tofind, &idx); if (mze == NULL) mze = avl_nearest(avl, idx, AVL_AFTER); @@ -303,10 +333,7 @@ mze_find(zap_name_t *zn) if (zap_match(zn, MZE_PHYS(zn->zn_zap, mze)->mze_name)) return (mze); } - if (zn->zn_matchtype == MT_BEST) { - zn->zn_matchtype = MT_FIRST; - goto again; - } + return (NULL); } @@ -412,8 +439,7 @@ mzap_open(objset_t *os, uint64_t obj, dmu_buf_t *db) zap_name_t *zn; zap->zap_m.zap_num_entries++; - zn = zap_name_alloc(zap, mze->mze_name, - MT_EXACT); + zn = zap_name_alloc(zap, mze->mze_name, 0); mze_insert(zap, i, zn->zn_hash); zap_name_free(zn); } @@ -611,7 +637,7 @@ mzap_upgrade(zap_t **zapp, void *tag, dmu_tx_t *tx, zap_flags_t flags) continue; dprintf("adding %s=%llu\n", mze->mze_name, mze->mze_value); - zn = zap_name_alloc(zap, mze->mze_name, MT_EXACT); + zn = zap_name_alloc(zap, mze->mze_name, 0); err = fzap_add_cd(zn, 8, 1, &mze->mze_value, mze->mze_cd, tag, tx); zap = zn->zn_zap; /* fzap_add_cd() may change zap */ @@ -624,6 +650,23 @@ mzap_upgrade(zap_t **zapp, void *tag, dmu_tx_t *tx, zap_flags_t flags) return (err); } +/* + * The "normflags" determine the behavior of the matchtype_t which is + * passed to zap_lookup_norm(). Names which have the same normalized + * version will be stored with the same hash value, and therefore we can + * perform normalization-insensitive lookups. We can be Unicode form- + * insensitive and/or case-insensitive. The following flags are valid for + * "normflags": + * + * U8_TEXTPREP_NFC + * U8_TEXTPREP_NFD + * U8_TEXTPREP_NFKC + * U8_TEXTPREP_NFKD + * U8_TEXTPREP_TOUPPER + * + * The *_NF* (Normalization Form) flags are mutually exclusive; at most one + * of them may be supplied. + */ void mzap_create_impl(objset_t *os, uint64_t obj, int normflags, zap_flags_t flags, dmu_tx_t *tx) @@ -827,7 +870,7 @@ mzap_normalization_conflict(zap_t *zap, zap_name_t *zn, mzap_ent_t *mze) if (zn == NULL) { zn = zap_name_alloc(zap, MZE_PHYS(zap, mze)->mze_name, - MT_FIRST); + MT_NORMALIZE); allocdzn = B_TRUE; } if (zap_match(zn, MZE_PHYS(zap, other)->mze_name)) { @@ -856,7 +899,7 @@ zap_lookup(objset_t *os, uint64_t zapobj, const char *name, uint64_t integer_size, uint64_t num_integers, void *buf) { return (zap_lookup_norm(os, zapobj, name, integer_size, - num_integers, buf, MT_EXACT, NULL, 0, NULL)); + num_integers, buf, 0, NULL, 0, NULL)); } static int @@ -929,7 +972,7 @@ zap_prefetch(objset_t *os, uint64_t zapobj, const char *name) err = zap_lockdir(os, zapobj, NULL, RW_READER, TRUE, FALSE, FTAG, &zap); if (err) return (err); - zn = zap_name_alloc(zap, name, MT_EXACT); + zn = zap_name_alloc(zap, name, 0); if (zn == NULL) { zap_unlockdir(zap, FTAG); return (SET_ERROR(ENOTSUP)); @@ -946,7 +989,7 @@ zap_lookup_by_dnode(dnode_t *dn, const char *name, uint64_t integer_size, uint64_t num_integers, void *buf) { return (zap_lookup_norm_by_dnode(dn, name, integer_size, - num_integers, buf, MT_EXACT, NULL, 0, NULL)); + num_integers, buf, 0, NULL, 0, NULL)); } int @@ -1019,7 +1062,7 @@ int zap_contains(objset_t *os, uint64_t zapobj, const char *name) { int err = zap_lookup_norm(os, zapobj, name, 0, - 0, NULL, MT_EXACT, NULL, 0, NULL); + 0, NULL, 0, NULL, 0, NULL); if (err == EOVERFLOW || err == EINVAL) err = 0; /* found, but skipped reading the value */ return (err); @@ -1037,7 +1080,7 @@ zap_length(objset_t *os, uint64_t zapobj, const char *name, err = zap_lockdir(os, zapobj, NULL, RW_READER, TRUE, FALSE, FTAG, &zap); if (err) return (err); - zn = zap_name_alloc(zap, name, MT_EXACT); + zn = zap_name_alloc(zap, name, 0); if (zn == NULL) { zap_unlockdir(zap, FTAG); return (SET_ERROR(ENOTSUP)); @@ -1138,7 +1181,7 @@ zap_add_impl(zap_t *zap, const char *key, const uint64_t *intval = val; zap_name_t *zn; - zn = zap_name_alloc(zap, key, MT_EXACT); + zn = zap_name_alloc(zap, key, 0); if (zn == NULL) { zap_unlockdir(zap, tag); return (SET_ERROR(ENOTSUP)); @@ -1249,7 +1292,7 @@ zap_update(objset_t *os, uint64_t zapobj, const char *name, err = zap_lockdir(os, zapobj, tx, RW_WRITER, TRUE, TRUE, FTAG, &zap); if (err) return (err); - zn = zap_name_alloc(zap, name, MT_EXACT); + zn = zap_name_alloc(zap, name, 0); if (zn == NULL) { zap_unlockdir(zap, FTAG); return (SET_ERROR(ENOTSUP)); @@ -1312,7 +1355,7 @@ zap_update_uint64(objset_t *os, uint64_t zapobj, const uint64_t *key, int zap_remove(objset_t *os, uint64_t zapobj, const char *name, dmu_tx_t *tx) { - return (zap_remove_norm(os, zapobj, name, MT_EXACT, tx)); + return (zap_remove_norm(os, zapobj, name, 0, tx)); } static int @@ -1367,7 +1410,7 @@ zap_remove_by_dnode(dnode_t *dn, const char *name, dmu_tx_t *tx) err = zap_lockdir_by_dnode(dn, tx, RW_WRITER, TRUE, FALSE, FTAG, &zap); if (err) return (err); - err = zap_remove_impl(zap, name, MT_EXACT, tx); + err = zap_remove_impl(zap, name, 0, tx); zap_unlockdir(zap, FTAG); return (err); } @@ -1586,7 +1629,7 @@ zap_count_write_by_dnode(dnode_t *dn, const char *name, int add, return (err); if (!zap->zap_ismicro) { - zap_name_t *zn = zap_name_alloc(zap, name, MT_EXACT); + zap_name_t *zn = zap_name_alloc(zap, name, 0); if (zn) { err = fzap_count_write(zn, add, towrite, tooverwrite); diff --git a/module/zfs/zfs_dir.c b/module/zfs/zfs_dir.c index 22e64d393ee9..c5643c43a917 100644 --- a/module/zfs/zfs_dir.c +++ b/module/zfs/zfs_dir.c @@ -18,9 +18,11 @@ * * CDDL HEADER END */ + /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013, 2015 by Delphix. All rights reserved. + * Copyright 2017 Nexenta Systems, Inc. */ @@ -64,14 +66,13 @@ * of names after deciding which is the appropriate lookup interface. */ static int -zfs_match_find(zfs_sb_t *zsb, znode_t *dzp, char *name, boolean_t exact, +zfs_match_find(zfs_sb_t *zsb, znode_t *dzp, char *name, matchtype_t mt, boolean_t update, int *deflags, pathname_t *rpnp, uint64_t *zoid) { boolean_t conflict = B_FALSE; int error; if (zsb->z_norm) { - matchtype_t mt = MT_FIRST; size_t bufsz = 0; char *buf = NULL; @@ -79,8 +80,7 @@ zfs_match_find(zfs_sb_t *zsb, znode_t *dzp, char *name, boolean_t exact, buf = rpnp->pn_buf; bufsz = rpnp->pn_bufsize; } - if (exact) - mt = MT_EXACT; + /* * In the non-mixed case we only expect there would ever * be one match, but we need to use the normalizing lookup. @@ -156,7 +156,7 @@ zfs_dirent_lock(zfs_dirlock_t **dlpp, znode_t *dzp, char *name, znode_t **zpp, zfs_sb_t *zsb = ZTOZSB(dzp); zfs_dirlock_t *dl; boolean_t update; - boolean_t exact; + matchtype_t mt = 0; uint64_t zoid; #ifdef HAVE_DNLC vnode_t *vp = NULL; @@ -193,13 +193,29 @@ zfs_dirent_lock(zfs_dirlock_t **dlpp, znode_t *dzp, char *name, znode_t **zpp, */ /* - * Decide if exact matches should be requested when performing - * a zap lookup on file systems supporting case-insensitive - * access. + * When matching we may need to normalize & change case according to + * FS settings. + * + * Note that a normalized match is necessary for a case insensitive + * filesystem when the lookup request is not exact because normalization + * can fold case independent of normalizing code point sequences. + * + * See the table above zfs_dropname(). */ - exact = - ((zsb->z_case == ZFS_CASE_INSENSITIVE) && (flag & ZCIEXACT)) || - ((zsb->z_case == ZFS_CASE_MIXED) && !(flag & ZCILOOK)); + if (zsb->z_norm != 0) { + mt = MT_NORMALIZE; + + /* + * Determine if the match needs to honor the case specified in + * lookup, and if so keep track of that so that during + * normalization we don't fold case. + */ + if ((zsb->z_case == ZFS_CASE_INSENSITIVE && + (flag & ZCIEXACT)) || + (zsb->z_case == ZFS_CASE_MIXED && !(flag & ZCILOOK))) { + mt |= MT_MATCH_CASE; + } + } /* * Only look in or update the DNLC if we are looking for the @@ -212,7 +228,7 @@ zfs_dirent_lock(zfs_dirlock_t **dlpp, znode_t *dzp, char *name, znode_t **zpp, * case for performance improvement? */ update = !zsb->z_norm || - ((zsb->z_case == ZFS_CASE_MIXED) && + (zsb->z_case == ZFS_CASE_MIXED && !(zsb->z_norm & ~U8_TEXTPREP_TOUPPER) && !(flag & ZCILOOK)); /* @@ -327,11 +343,11 @@ zfs_dirent_lock(zfs_dirlock_t **dlpp, znode_t *dzp, char *name, znode_t **zpp, *zpp = VTOZ(vp); return (0); } else { - error = zfs_match_find(zsb, dzp, name, exact, + error = zfs_match_find(zsb, dzp, name, mt, update, direntflags, realpnp, &zoid); } #else - error = zfs_match_find(zsb, dzp, name, exact, + error = zfs_match_find(zsb, dzp, name, mt, update, direntflags, realpnp, &zoid); #endif /* HAVE_DNLC */ } @@ -805,6 +821,28 @@ zfs_link_create(zfs_dirlock_t *dl, znode_t *zp, dmu_tx_t *tx, int flag) return (0); } +/* + * The match type in the code for this function should conform to: + * + * ------------------------------------------------------------------------ + * fs type | z_norm | lookup type | match type + * ---------|-------------|-------------|---------------------------------- + * CS !norm | 0 | 0 | 0 (exact) + * CS norm | formX | 0 | MT_NORMALIZE + * CI !norm | upper | !ZCIEXACT | MT_NORMALIZE + * CI !norm | upper | ZCIEXACT | MT_NORMALIZE | MT_MATCH_CASE + * CI norm | upper|formX | !ZCIEXACT | MT_NORMALIZE + * CI norm | upper|formX | ZCIEXACT | MT_NORMALIZE | MT_MATCH_CASE + * CM !norm | upper | !ZCILOOK | MT_NORMALIZE | MT_MATCH_CASE + * CM !norm | upper | ZCILOOK | MT_NORMALIZE + * CM norm | upper|formX | !ZCILOOK | MT_NORMALIZE | MT_MATCH_CASE + * CM norm | upper|formX | ZCILOOK | MT_NORMALIZE + * + * Abbreviations: + * CS = Case Sensitive, CI = Case Insensitive, CM = Case Mixed + * upper = case folding set by fs type on creation (U8_TEXTPREP_TOUPPER) + * formX = unicode normalization form set on fs creation + */ static int zfs_dropname(zfs_dirlock_t *dl, znode_t *zp, znode_t *dzp, dmu_tx_t *tx, int flag) @@ -812,18 +850,20 @@ zfs_dropname(zfs_dirlock_t *dl, znode_t *zp, znode_t *dzp, dmu_tx_t *tx, int error; if (ZTOZSB(zp)->z_norm) { - if (((ZTOZSB(zp)->z_case == ZFS_CASE_INSENSITIVE) && + matchtype_t mt = MT_NORMALIZE; + + if ((ZTOZSB(zp)->z_case == ZFS_CASE_INSENSITIVE && (flag & ZCIEXACT)) || - ((ZTOZSB(zp)->z_case == ZFS_CASE_MIXED) && - !(flag & ZCILOOK))) - error = zap_remove_norm(ZTOZSB(zp)->z_os, - dzp->z_id, dl->dl_name, MT_EXACT, tx); - else - error = zap_remove_norm(ZTOZSB(zp)->z_os, - dzp->z_id, dl->dl_name, MT_FIRST, tx); + (ZTOZSB(zp)->z_case == ZFS_CASE_MIXED && + !(flag & ZCILOOK))) { + mt |= MT_MATCH_CASE; + } + + error = zap_remove_norm(ZTOZSB(zp)->z_os, dzp->z_id, + dl->dl_name, mt, tx); } else { - error = zap_remove(ZTOZSB(zp)->z_os, - dzp->z_id, dl->dl_name, tx); + error = zap_remove(ZTOZSB(zp)->z_os, dzp->z_id, dl->dl_name, + tx); } return (error); diff --git a/module/zfs/zfs_vnops.c b/module/zfs/zfs_vnops.c index 7344c9a80cf2..b55445e0a08e 100644 --- a/module/zfs/zfs_vnops.c +++ b/module/zfs/zfs_vnops.c @@ -18,11 +18,12 @@ * * CDDL HEADER END */ + /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2015 by Delphix. All rights reserved. - * Copyright 2014 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2015 by Chunwei Chen. All rights reserved. + * Copyright 2017 Nexenta Systems, Inc. */ /* Portions Copyright 2007 Jeremy Teo */ @@ -1157,7 +1158,15 @@ zfs_lookup(struct inode *dip, char *nm, struct inode **ipp, int flags, zfs_sb_t *zsb = ITOZSB(dip); int error = 0; - /* fast path */ + /* + * Fast path lookup, however we must skip DNLC lookup + * for case folding or normalizing lookups because the + * DNLC code only stores the passed in name. This means + * creating 'a' and removing 'A' on a case insensitive + * file system would work, but DNLC still thinks 'a' + * exists and won't let you create it again on the next + * pass through fast path. + */ if (!(flags & (LOOKUP_XATTR | FIGNORECASE))) { if (!S_ISDIR(dip->i_mode)) { @@ -1175,7 +1184,9 @@ zfs_lookup(struct inode *dip, char *nm, struct inode **ipp, int flags, } return (error); #ifdef HAVE_DNLC - } else { + } else if (!zdp->z_zfsvfs->z_norm && + (zdp->z_zfsvfs->z_case == ZFS_CASE_SENSITIVE)) { + vnode_t *tvp = dnlc_lookup(dvp, nm); if (tvp) {