OpenZFS 1300 - filename normalization doesn't work for removes

Authored by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Reviewed by: Pavel Zakharov <pavel.zakharov@delphix.com>
Reviewed by: Matt Ahrens <mahrens@delphix.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Ported-by: George Melikov <mail@gmelikov.ru>

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`
This commit is contained in:
George Melikov 2017-02-03 01:13:41 +03:00 committed by Brian Behlendorf
parent 96f1b347f8
commit 9b7b9cd370
9 changed files with 185 additions and 117 deletions

View file

@ -18,9 +18,11 @@
* *
* CDDL HEADER END * CDDL HEADER END
*/ */
/* /*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013 by Delphix. All rights reserved. * Copyright (c) 2013 by Delphix. All rights reserved.
* Copyright 2017 Nexenta Systems, Inc.
*/ */
#ifndef _SYS_ZAP_H #ifndef _SYS_ZAP_H
@ -87,22 +89,15 @@ extern "C" {
/* /*
* Specifies matching criteria for ZAP lookups. * 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 typedef enum matchtype {
{ MT_NORMALIZE = 1 << 0,
/* Only find an exact match (non-normalized) */ MT_MATCH_CASE = 1 << 1,
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
} matchtype_t; } matchtype_t;
typedef enum zap_flags { typedef enum zap_flags {
@ -119,19 +114,6 @@ typedef enum zap_flags {
/* /*
* Create a new zapobj with no attributes and return its object number. * 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, uint64_t zap_create(objset_t *ds, dmu_object_type_t ot,
dmu_object_type_t bonustype, int bonuslen, dmu_tx_t *tx); dmu_object_type_t bonustype, int bonuslen, dmu_tx_t *tx);

View file

@ -18,10 +18,12 @@
* *
* CDDL HEADER END * CDDL HEADER END
*/ */
/* /*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved. * Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
* Copyright (c) 2013, 2016 by Delphix. All rights reserved. * Copyright (c) 2013, 2016 by Delphix. All rights reserved.
* Copyright 2017 Nexenta Systems, Inc.
*/ */
#ifndef _SYS_ZAP_IMPL_H #ifndef _SYS_ZAP_IMPL_H
@ -188,6 +190,7 @@ typedef struct zap_name {
int zn_key_norm_numints; int zn_key_norm_numints;
uint64_t zn_hash; uint64_t zn_hash;
matchtype_t zn_matchtype; matchtype_t zn_matchtype;
int zn_normflags;
char zn_normbuf[ZAP_MAXNAMELEN]; char zn_normbuf[ZAP_MAXNAMELEN];
} zap_name_t; } zap_name_t;

View file

@ -18,15 +18,16 @@
* *
* CDDL HEADER END * CDDL HEADER END
*/ */
/* /*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2016 by Delphix. All rights reserved. * Copyright (c) 2012, 2016 by Delphix. All rights reserved.
* Copyright (c) 2013 by Saso Kiselkov. All rights reserved. * Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
* Copyright (c) 2013, Joyent, Inc. All rights reserved. * Copyright (c) 2013, Joyent, Inc. All rights reserved.
* Copyright (c) 2014 Spectra Logic Corporation, 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) 2015, STRATO AG, Inc. All rights reserved.
* Copyright (c) 2016 Actifio, Inc. All rights reserved. * Copyright (c) 2016 Actifio, Inc. All rights reserved.
* Copyright 2017 Nexenta Systems, Inc.
*/ */
/* Portions Copyright 2010 Robert Milkowski */ /* 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, return (zap_lookup_norm(ds->ds_dir->dd_pool->dp_meta_objset,
dsl_dataset_phys(ds)->ds_snapnames_zapobj, name, 8, 1, &ignored, dsl_dataset_phys(ds)->ds_snapnames_zapobj, name, 8, 1, &ignored,
MT_FIRST, real, maxlen, conflict)); MT_NORMALIZE, real, maxlen, conflict));
} }
int int

View file

@ -12,8 +12,10 @@
* *
* CDDL HEADER END * CDDL HEADER END
*/ */
/* /*
* Copyright (c) 2013, 2014 by Delphix. All rights reserved. * Copyright (c) 2013, 2014 by Delphix. All rights reserved.
* Copyright 2017 Nexenta Systems, Inc.
*/ */
#include <sys/zfs_context.h> #include <sys/zfs_context.h>
@ -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; objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
uint64_t bmark_zapobj = ds->ds_bookmarks; uint64_t bmark_zapobj = ds->ds_bookmarks;
matchtype_t mt; matchtype_t mt = 0;
int err; int err;
if (bmark_zapobj == 0) if (bmark_zapobj == 0)
return (SET_ERROR(ESRCH)); return (SET_ERROR(ESRCH));
if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET) if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET)
mt = MT_FIRST; mt = MT_NORMALIZE;
else
mt = MT_EXACT;
err = zap_lookup_norm(mos, bmark_zapobj, shortname, sizeof (uint64_t), err = zap_lookup_norm(mos, bmark_zapobj, shortname, sizeof (uint64_t),
sizeof (*bmark_phys) / sizeof (uint64_t), bmark_phys, mt, 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; objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
uint64_t bmark_zapobj = ds->ds_bookmarks; 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) if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET)
mt = MT_FIRST; mt = MT_NORMALIZE;
else
mt = MT_EXACT;
return (zap_remove_norm(mos, bmark_zapobj, name, mt, tx)); return (zap_remove_norm(mos, bmark_zapobj, name, mt, tx));
} }

View file

@ -18,6 +18,7 @@
* *
* CDDL HEADER END * CDDL HEADER END
*/ */
/* /*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2016 by Delphix. 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) 2014 Spectra Logic Corporation, All rights reserved.
* Copyright (c) 2016 Actifio, Inc. All rights reserved. * Copyright (c) 2016 Actifio, Inc. All rights reserved.
* Copyright 2016, OmniTI Computer Consulting, Inc. All rights reserved. * Copyright 2016, OmniTI Computer Consulting, Inc. All rights reserved.
* Copyright 2017 Nexenta Systems, Inc.
*/ */
#include <sys/dmu_objset.h> #include <sys/dmu_objset.h>
@ -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; objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
uint64_t snapobj = dsl_dataset_phys(ds)->ds_snapnames_zapobj; uint64_t snapobj = dsl_dataset_phys(ds)->ds_snapnames_zapobj;
matchtype_t mt; matchtype_t mt = 0;
int err; int err;
if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET) if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET)
mt = MT_FIRST; mt = MT_NORMALIZE;
else
mt = MT_EXACT;
err = zap_lookup_norm(mos, snapobj, name, 8, 1, err = zap_lookup_norm(mos, snapobj, name, 8, 1,
value, mt, NULL, 0, NULL); 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); err = zap_lookup(mos, snapobj, name, 8, 1, value);
return (err); 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; objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset;
uint64_t snapobj = dsl_dataset_phys(ds)->ds_snapnames_zapobj; uint64_t snapobj = dsl_dataset_phys(ds)->ds_snapnames_zapobj;
matchtype_t mt; matchtype_t mt = 0;
int err; int err;
dsl_dir_snap_cmtime_update(ds->ds_dir); dsl_dir_snap_cmtime_update(ds->ds_dir);
if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET) if (dsl_dataset_phys(ds)->ds_flags & DS_FLAG_CI_DATASET)
mt = MT_FIRST; mt = MT_NORMALIZE;
else
mt = MT_EXACT;
err = zap_remove_norm(mos, snapobj, name, mt, tx); 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); err = zap_remove(mos, snapobj, name, tx);
if (err == 0 && adj_cnt) if (err == 0 && adj_cnt)

View file

@ -18,9 +18,11 @@
* *
* CDDL HEADER END * CDDL HEADER END
*/ */
/* /*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2015 by Delphix. 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); 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); char *thisname = kmem_alloc(array_numints, KM_SLEEP);
boolean_t match; 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); ASSERT3U(zap_leaf_phys(l)->l_hdr.lh_magic, ==, ZAP_LEAF_MAGIC);
again:
for (chunkp = LEAF_HASH_ENTPTR(l, zn->zn_hash); for (chunkp = LEAF_HASH_ENTPTR(l, zn->zn_hash);
*chunkp != CHAIN_END; chunkp = &le->le_next) { *chunkp != CHAIN_END; chunkp = &le->le_next) {
uint16_t chunk = *chunkp; 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 * NB: the entry chain is always sorted by cd on
* normalized zap objects, so this will find the * 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)); (zap_leaf_phys(l)->l_hdr.lh_flags & ZLF_ENTRIES_CDSORTED));
if (zap_leaf_array_match(l, zn, le->le_name_chunk, if (zap_leaf_array_match(l, zn, le->le_name_chunk,
le->le_name_numints)) { 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)); return (SET_ERROR(ENOENT));
} }
@ -700,7 +692,7 @@ zap_entry_normalization_conflict(zap_entry_handle_t *zeh, zap_name_t *zn,
continue; continue;
if (zn == NULL) { if (zn == NULL) {
zn = zap_name_alloc(zap, name, MT_FIRST); zn = zap_name_alloc(zap, name, MT_NORMALIZE);
allocdzn = B_TRUE; allocdzn = B_TRUE;
} }
if (zap_leaf_array_match(zeh->zeh_leaf, zn, if (zap_leaf_array_match(zeh->zeh_leaf, zn,

View file

@ -18,10 +18,12 @@
* *
* CDDL HEADER END * CDDL HEADER END
*/ */
/* /*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2016 by Delphix. All rights reserved. * Copyright (c) 2011, 2016 by Delphix. All rights reserved.
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved. * Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
* Copyright 2017 Nexenta Systems, Inc.
*/ */
#include <sys/zio.h> #include <sys/zio.h>
@ -132,7 +134,7 @@ zap_hash(zap_name_t *zn)
} }
static int 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; size_t inlen, outlen;
int err; int err;
@ -144,8 +146,8 @@ zap_normalize(zap_t *zap, const char *name, char *namenorm)
err = 0; err = 0;
(void) u8_textprep_str((char *)name, &inlen, namenorm, &outlen, (void) u8_textprep_str((char *)name, &inlen, namenorm, &outlen,
zap->zap_normflags | U8_TEXTPREP_IGNORE_NULL | normflags | U8_TEXTPREP_IGNORE_NULL | U8_TEXTPREP_IGNORE_INVALID,
U8_TEXTPREP_IGNORE_INVALID, U8_UNICODE_LATEST, &err); U8_UNICODE_LATEST, &err);
return (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)); 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]; 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 (B_FALSE);
return (strcmp(zn->zn_key_norm, norm) == 0); return (strcmp(zn->zn_key_norm, norm) == 0);
} else { } else {
/* MT_BEST or MT_EXACT */
return (strcmp(zn->zn_key_orig, matchname) == 0); 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 = key;
zn->zn_key_orig_numints = strlen(zn->zn_key_orig) + 1; zn->zn_key_orig_numints = strlen(zn->zn_key_orig) + 1;
zn->zn_matchtype = mt; 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->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); zap_name_free(zn);
return (NULL); return (NULL);
} }
zn->zn_key_norm = zn->zn_normbuf; zn->zn_key_norm = zn->zn_normbuf;
zn->zn_key_norm_numints = strlen(zn->zn_key_norm) + 1; zn->zn_key_norm_numints = strlen(zn->zn_key_norm) + 1;
} else { } else {
if (mt != MT_EXACT) { if (mt != 0) {
zap_name_free(zn); zap_name_free(zn);
return (NULL); return (NULL);
} }
@ -201,6 +218,20 @@ zap_name_alloc(zap_t *zap, const char *key, matchtype_t mt)
} }
zn->zn_hash = zap_hash(zn); 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); 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_intlen = sizeof (*key);
zn->zn_key_orig = zn->zn_key_norm = key; zn->zn_key_orig = zn->zn_key_norm = key;
zn->zn_key_orig_numints = zn->zn_key_norm_numints = numints; 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); zn->zn_hash = zap_hash(zn);
return (zn); return (zn);
@ -294,7 +325,6 @@ mze_find(zap_name_t *zn)
mze_tofind.mze_hash = zn->zn_hash; mze_tofind.mze_hash = zn->zn_hash;
mze_tofind.mze_cd = 0; mze_tofind.mze_cd = 0;
again:
mze = avl_find(avl, &mze_tofind, &idx); mze = avl_find(avl, &mze_tofind, &idx);
if (mze == NULL) if (mze == NULL)
mze = avl_nearest(avl, idx, AVL_AFTER); 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)) if (zap_match(zn, MZE_PHYS(zn->zn_zap, mze)->mze_name))
return (mze); return (mze);
} }
if (zn->zn_matchtype == MT_BEST) {
zn->zn_matchtype = MT_FIRST;
goto again;
}
return (NULL); return (NULL);
} }
@ -412,8 +439,7 @@ mzap_open(objset_t *os, uint64_t obj, dmu_buf_t *db)
zap_name_t *zn; zap_name_t *zn;
zap->zap_m.zap_num_entries++; zap->zap_m.zap_num_entries++;
zn = zap_name_alloc(zap, mze->mze_name, zn = zap_name_alloc(zap, mze->mze_name, 0);
MT_EXACT);
mze_insert(zap, i, zn->zn_hash); mze_insert(zap, i, zn->zn_hash);
zap_name_free(zn); zap_name_free(zn);
} }
@ -611,7 +637,7 @@ mzap_upgrade(zap_t **zapp, void *tag, dmu_tx_t *tx, zap_flags_t flags)
continue; continue;
dprintf("adding %s=%llu\n", dprintf("adding %s=%llu\n",
mze->mze_name, mze->mze_value); 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, err = fzap_add_cd(zn, 8, 1, &mze->mze_value, mze->mze_cd,
tag, tx); tag, tx);
zap = zn->zn_zap; /* fzap_add_cd() may change zap */ 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); 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 void
mzap_create_impl(objset_t *os, uint64_t obj, int normflags, zap_flags_t flags, mzap_create_impl(objset_t *os, uint64_t obj, int normflags, zap_flags_t flags,
dmu_tx_t *tx) 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) { if (zn == NULL) {
zn = zap_name_alloc(zap, MZE_PHYS(zap, mze)->mze_name, zn = zap_name_alloc(zap, MZE_PHYS(zap, mze)->mze_name,
MT_FIRST); MT_NORMALIZE);
allocdzn = B_TRUE; allocdzn = B_TRUE;
} }
if (zap_match(zn, MZE_PHYS(zap, other)->mze_name)) { 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) uint64_t integer_size, uint64_t num_integers, void *buf)
{ {
return (zap_lookup_norm(os, zapobj, name, integer_size, 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 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); err = zap_lockdir(os, zapobj, NULL, RW_READER, TRUE, FALSE, FTAG, &zap);
if (err) if (err)
return (err); return (err);
zn = zap_name_alloc(zap, name, MT_EXACT); zn = zap_name_alloc(zap, name, 0);
if (zn == NULL) { if (zn == NULL) {
zap_unlockdir(zap, FTAG); zap_unlockdir(zap, FTAG);
return (SET_ERROR(ENOTSUP)); 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) uint64_t integer_size, uint64_t num_integers, void *buf)
{ {
return (zap_lookup_norm_by_dnode(dn, name, integer_size, 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 int
@ -1019,7 +1062,7 @@ int
zap_contains(objset_t *os, uint64_t zapobj, const char *name) zap_contains(objset_t *os, uint64_t zapobj, const char *name)
{ {
int err = zap_lookup_norm(os, zapobj, name, 0, 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) if (err == EOVERFLOW || err == EINVAL)
err = 0; /* found, but skipped reading the value */ err = 0; /* found, but skipped reading the value */
return (err); 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); err = zap_lockdir(os, zapobj, NULL, RW_READER, TRUE, FALSE, FTAG, &zap);
if (err) if (err)
return (err); return (err);
zn = zap_name_alloc(zap, name, MT_EXACT); zn = zap_name_alloc(zap, name, 0);
if (zn == NULL) { if (zn == NULL) {
zap_unlockdir(zap, FTAG); zap_unlockdir(zap, FTAG);
return (SET_ERROR(ENOTSUP)); return (SET_ERROR(ENOTSUP));
@ -1138,7 +1181,7 @@ zap_add_impl(zap_t *zap, const char *key,
const uint64_t *intval = val; const uint64_t *intval = val;
zap_name_t *zn; zap_name_t *zn;
zn = zap_name_alloc(zap, key, MT_EXACT); zn = zap_name_alloc(zap, key, 0);
if (zn == NULL) { if (zn == NULL) {
zap_unlockdir(zap, tag); zap_unlockdir(zap, tag);
return (SET_ERROR(ENOTSUP)); 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); err = zap_lockdir(os, zapobj, tx, RW_WRITER, TRUE, TRUE, FTAG, &zap);
if (err) if (err)
return (err); return (err);
zn = zap_name_alloc(zap, name, MT_EXACT); zn = zap_name_alloc(zap, name, 0);
if (zn == NULL) { if (zn == NULL) {
zap_unlockdir(zap, FTAG); zap_unlockdir(zap, FTAG);
return (SET_ERROR(ENOTSUP)); return (SET_ERROR(ENOTSUP));
@ -1312,7 +1355,7 @@ zap_update_uint64(objset_t *os, uint64_t zapobj, const uint64_t *key,
int int
zap_remove(objset_t *os, uint64_t zapobj, const char *name, dmu_tx_t *tx) 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 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); err = zap_lockdir_by_dnode(dn, tx, RW_WRITER, TRUE, FALSE, FTAG, &zap);
if (err) if (err)
return (err); return (err);
err = zap_remove_impl(zap, name, MT_EXACT, tx); err = zap_remove_impl(zap, name, 0, tx);
zap_unlockdir(zap, FTAG); zap_unlockdir(zap, FTAG);
return (err); return (err);
} }
@ -1586,7 +1629,7 @@ zap_count_write_by_dnode(dnode_t *dn, const char *name, int add,
return (err); return (err);
if (!zap->zap_ismicro) { 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) { if (zn) {
err = fzap_count_write(zn, add, towrite, err = fzap_count_write(zn, add, towrite,
tooverwrite); tooverwrite);

View file

@ -18,9 +18,11 @@
* *
* CDDL HEADER END * CDDL HEADER END
*/ */
/* /*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2015 by Delphix. 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. * of names after deciding which is the appropriate lookup interface.
*/ */
static int 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 update, int *deflags, pathname_t *rpnp, uint64_t *zoid)
{ {
boolean_t conflict = B_FALSE; boolean_t conflict = B_FALSE;
int error; int error;
if (zsb->z_norm) { if (zsb->z_norm) {
matchtype_t mt = MT_FIRST;
size_t bufsz = 0; size_t bufsz = 0;
char *buf = NULL; 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; buf = rpnp->pn_buf;
bufsz = rpnp->pn_bufsize; bufsz = rpnp->pn_bufsize;
} }
if (exact)
mt = MT_EXACT;
/* /*
* In the non-mixed case we only expect there would ever * In the non-mixed case we only expect there would ever
* be one match, but we need to use the normalizing lookup. * 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_sb_t *zsb = ZTOZSB(dzp);
zfs_dirlock_t *dl; zfs_dirlock_t *dl;
boolean_t update; boolean_t update;
boolean_t exact; matchtype_t mt = 0;
uint64_t zoid; uint64_t zoid;
#ifdef HAVE_DNLC #ifdef HAVE_DNLC
vnode_t *vp = NULL; 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 * When matching we may need to normalize & change case according to
* a zap lookup on file systems supporting case-insensitive * FS settings.
* access. *
* 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 = if (zsb->z_norm != 0) {
((zsb->z_case == ZFS_CASE_INSENSITIVE) && (flag & ZCIEXACT)) || mt = MT_NORMALIZE;
((zsb->z_case == ZFS_CASE_MIXED) && !(flag & ZCILOOK));
/*
* 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 * 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? * case for performance improvement?
*/ */
update = !zsb->z_norm || update = !zsb->z_norm ||
((zsb->z_case == ZFS_CASE_MIXED) && (zsb->z_case == ZFS_CASE_MIXED &&
!(zsb->z_norm & ~U8_TEXTPREP_TOUPPER) && !(flag & ZCILOOK)); !(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); *zpp = VTOZ(vp);
return (0); return (0);
} else { } else {
error = zfs_match_find(zsb, dzp, name, exact, error = zfs_match_find(zsb, dzp, name, mt,
update, direntflags, realpnp, &zoid); update, direntflags, realpnp, &zoid);
} }
#else #else
error = zfs_match_find(zsb, dzp, name, exact, error = zfs_match_find(zsb, dzp, name, mt,
update, direntflags, realpnp, &zoid); update, direntflags, realpnp, &zoid);
#endif /* HAVE_DNLC */ #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); 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 static int
zfs_dropname(zfs_dirlock_t *dl, znode_t *zp, znode_t *dzp, dmu_tx_t *tx, zfs_dropname(zfs_dirlock_t *dl, znode_t *zp, znode_t *dzp, dmu_tx_t *tx,
int flag) int flag)
@ -812,18 +850,20 @@ zfs_dropname(zfs_dirlock_t *dl, znode_t *zp, znode_t *dzp, dmu_tx_t *tx,
int error; int error;
if (ZTOZSB(zp)->z_norm) { 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)) || (flag & ZCIEXACT)) ||
((ZTOZSB(zp)->z_case == ZFS_CASE_MIXED) && (ZTOZSB(zp)->z_case == ZFS_CASE_MIXED &&
!(flag & ZCILOOK))) !(flag & ZCILOOK))) {
error = zap_remove_norm(ZTOZSB(zp)->z_os, mt |= MT_MATCH_CASE;
dzp->z_id, dl->dl_name, MT_EXACT, tx); }
else
error = zap_remove_norm(ZTOZSB(zp)->z_os, error = zap_remove_norm(ZTOZSB(zp)->z_os, dzp->z_id,
dzp->z_id, dl->dl_name, MT_FIRST, tx); dl->dl_name, mt, tx);
} else { } else {
error = zap_remove(ZTOZSB(zp)->z_os, error = zap_remove(ZTOZSB(zp)->z_os, dzp->z_id, dl->dl_name,
dzp->z_id, dl->dl_name, tx); tx);
} }
return (error); return (error);

View file

@ -18,11 +18,12 @@
* *
* CDDL HEADER END * CDDL HEADER END
*/ */
/* /*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2015 by Delphix. 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 (c) 2015 by Chunwei Chen. All rights reserved.
* Copyright 2017 Nexenta Systems, Inc.
*/ */
/* Portions Copyright 2007 Jeremy Teo */ /* 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); zfs_sb_t *zsb = ITOZSB(dip);
int error = 0; 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 (!(flags & (LOOKUP_XATTR | FIGNORECASE))) {
if (!S_ISDIR(dip->i_mode)) { if (!S_ISDIR(dip->i_mode)) {
@ -1175,7 +1184,9 @@ zfs_lookup(struct inode *dip, char *nm, struct inode **ipp, int flags,
} }
return (error); return (error);
#ifdef HAVE_DNLC #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); vnode_t *tvp = dnlc_lookup(dvp, nm);
if (tvp) { if (tvp) {