* 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.
+ * or https://opensource.org/licenses/CDDL-1.0.
* See the License for the specific language governing permissions
* and limitations under the License.
*
#include <sys/file.h>
#include <sys/kmem.h>
#include <sys/errno.h>
-#include <sys/mode.h>
#include <sys/atomic.h>
#include <sys/zfs_dir.h>
#include <sys/zfs_acl.h>
#include <sys/dmu.h>
#include <sys/dmu_objset.h>
#include <sys/dmu_tx.h>
-#include <sys/refcount.h>
+#include <sys/zfs_refcount.h>
#include <sys/stat.h>
#include <sys/zap.h>
#include <sys/zfs_znode.h>
* This is used by the test suite so that it can delay znodes from being
* freed in order to inspect the unlinked set.
*/
-int zfs_unlink_suspend_progress = 0;
+static int zfs_unlink_suspend_progress = 0;
/*
* This callback is invoked when acquiring a RL_WRITER or RL_APPEND lock on
* called with the rangelock_t's rl_lock held, which avoids races.
*/
static void
-zfs_rangelock_cb(locked_range_t *new, void *arg)
+zfs_rangelock_cb(zfs_locked_range_t *new, void *arg)
{
znode_t *zp = arg;
}
}
-/*ARGSUSED*/
static int
zfs_znode_cache_constructor(void *buf, void *arg, int kmflags)
{
+ (void) arg, (void) kmflags;
znode_t *zp = buf;
inode_init_once(ZTOI(zp));
zp->z_acl_cached = NULL;
zp->z_xattr_cached = NULL;
zp->z_xattr_parent = 0;
- zp->z_moved = B_FALSE;
+ zp->z_sync_writes_cnt = 0;
+ zp->z_async_writes_cnt = 0;
+
return (0);
}
-/*ARGSUSED*/
static void
zfs_znode_cache_destructor(void *buf, void *arg)
{
+ (void) arg;
znode_t *zp = buf;
ASSERT(!list_link_active(&zp->z_link_node));
rw_destroy(&zp->z_xattr_lock);
zfs_rangelock_fini(&zp->z_rangelock);
- ASSERT(zp->z_dirlocks == NULL);
- ASSERT(zp->z_acl_cached == NULL);
- ASSERT(zp->z_xattr_cached == NULL);
+ ASSERT3P(zp->z_dirlocks, ==, NULL);
+ ASSERT3P(zp->z_acl_cached, ==, NULL);
+ ASSERT3P(zp->z_xattr_cached, ==, NULL);
+
+ ASSERT0(atomic_load_32(&zp->z_sync_writes_cnt));
+ ASSERT0(atomic_load_32(&zp->z_async_writes_cnt));
}
static int
zfs_znode_hold_cache_constructor(void *buf, void *arg, int kmflags)
{
+ (void) arg, (void) kmflags;
znode_hold_t *zh = buf;
mutex_init(&zh->zh_lock, NULL, MUTEX_DEFAULT, NULL);
- zfs_refcount_create(&zh->zh_refcount);
- zh->zh_obj = ZFS_NO_OBJECT;
+ zh->zh_refcount = 0;
return (0);
}
static void
zfs_znode_hold_cache_destructor(void *buf, void *arg)
{
+ (void) arg;
znode_hold_t *zh = buf;
mutex_destroy(&zh->zh_lock);
- zfs_refcount_destroy(&zh->zh_refcount);
}
void
* created or destroyed. This kind of locking would normally reside in the
* znode itself but in this case that's impossible because the znode and SA
* buffer may not yet exist. Therefore the locking is handled externally
- * with an array of mutexs and AVLs trees which contain per-object locks.
+ * with an array of mutexes and AVLs trees which contain per-object locks.
*
* In zfs_znode_hold_enter() a per-object lock is created as needed, inserted
* in to the correct AVL tree and finally the per-object lock is held. In
return (TREE_CMP(zh_a->zh_obj, zh_b->zh_obj));
}
-boolean_t
+static boolean_t __maybe_unused
zfs_znode_held(zfsvfs_t *zfsvfs, uint64_t obj)
{
znode_hold_t *zh, search;
return (held);
}
-static znode_hold_t *
+znode_hold_t *
zfs_znode_hold_enter(zfsvfs_t *zfsvfs, uint64_t obj)
{
znode_hold_t *zh, *zh_new, search;
boolean_t found = B_FALSE;
zh_new = kmem_cache_alloc(znode_hold_cache, KM_SLEEP);
- zh_new->zh_obj = obj;
search.zh_obj = obj;
mutex_enter(&zfsvfs->z_hold_locks[i]);
zh = avl_find(&zfsvfs->z_hold_trees[i], &search, NULL);
if (likely(zh == NULL)) {
zh = zh_new;
+ zh->zh_obj = obj;
avl_add(&zfsvfs->z_hold_trees[i], zh);
} else {
ASSERT3U(zh->zh_obj, ==, obj);
found = B_TRUE;
}
- zfs_refcount_add(&zh->zh_refcount, NULL);
+ zh->zh_refcount++;
+ ASSERT3S(zh->zh_refcount, >, 0);
mutex_exit(&zfsvfs->z_hold_locks[i]);
if (found == B_TRUE)
kmem_cache_free(znode_hold_cache, zh_new);
ASSERT(MUTEX_NOT_HELD(&zh->zh_lock));
- ASSERT3S(zfs_refcount_count(&zh->zh_refcount), >, 0);
mutex_enter(&zh->zh_lock);
return (zh);
}
-static void
+void
zfs_znode_hold_exit(zfsvfs_t *zfsvfs, znode_hold_t *zh)
{
int i = ZFS_OBJ_HASH(zfsvfs, zh->zh_obj);
boolean_t remove = B_FALSE;
ASSERT(zfs_znode_held(zfsvfs, zh->zh_obj));
- ASSERT3S(zfs_refcount_count(&zh->zh_refcount), >, 0);
mutex_exit(&zh->zh_lock);
mutex_enter(&zfsvfs->z_hold_locks[i]);
- if (zfs_refcount_remove(&zh->zh_refcount, NULL) == 0) {
+ ASSERT3S(zh->zh_refcount, >, 0);
+ if (--zh->zh_refcount == 0) {
avl_remove(&zfsvfs->z_hold_trees[i], zh);
remove = B_TRUE;
}
kmem_cache_free(znode_hold_cache, zh);
}
+dev_t
+zfs_cmpldev(uint64_t dev)
+{
+ return (dev);
+}
+
static void
zfs_znode_sa_init(zfsvfs_t *zfsvfs, znode_t *zp,
dmu_buf_t *db, dmu_object_type_t obj_type, sa_handle_t *sa_hdl)
void
zfs_znode_dmu_fini(znode_t *zp)
{
- ASSERT(zfs_znode_held(ZTOZSB(zp), zp->z_id) || zp->z_unlinked ||
+ ASSERT(zfs_znode_held(ZTOZSB(zp), zp->z_id) ||
RW_WRITE_HELD(&ZTOZSB(zp)->z_teardown_inactive_lock));
sa_handle_destroy(zp->z_sa_hdl);
mutex_enter(&zfsvfs->z_znodes_lock);
if (list_link_active(&zp->z_link_node)) {
list_remove(&zfsvfs->z_all_znodes, zp);
- zfsvfs->z_nr_znodes--;
}
mutex_exit(&zfsvfs->z_znodes_lock);
switch (ip->i_mode & S_IFMT) {
case S_IFREG:
ip->i_op = &zpl_inode_operations;
+#ifdef HAVE_VFS_FILE_OPERATIONS_EXTEND
+ ip->i_fop = &zpl_file_operations.kabi_fops;
+#else
ip->i_fop = &zpl_file_operations;
+#endif
ip->i_mapping->a_ops = &zpl_address_space_operations;
break;
case S_IFDIR:
+#ifdef HAVE_RENAME2_OPERATIONS_WRAPPER
+ ip->i_flags |= S_IOPS_WRAPPER;
+ ip->i_op = &zpl_dir_inode_operations.ops;
+#else
ip->i_op = &zpl_dir_inode_operations;
+#endif
ip->i_fop = &zpl_dir_file_operations;
ITOZ(ip)->z_zn_prefetch = B_TRUE;
break;
case S_IFBLK:
(void) sa_lookup(ITOZ(ip)->z_sa_hdl, SA_ZPL_RDEV(zfsvfs), &rdev,
sizeof (rdev));
- /*FALLTHROUGH*/
+ zfs_fallthrough;
case S_IFIFO:
case S_IFSOCK:
init_special_inode(ip, ip->i_mode, rdev);
/* Assume the inode is a file and attempt to continue */
ip->i_mode = S_IFREG | 0644;
ip->i_op = &zpl_inode_operations;
+#ifdef HAVE_VFS_FILE_OPERATIONS_EXTEND
+ ip->i_fop = &zpl_file_operations.kabi_fops;
+#else
ip->i_fop = &zpl_file_operations;
+#endif
ip->i_mapping->a_ops = &zpl_address_space_operations;
break;
}
}
-void
+static void
zfs_set_inode_flags(znode_t *zp, struct inode *ip)
{
/*
}
/*
- * Update the embedded inode given the znode. We should work toward
- * eliminating this function as soon as possible by removing values
- * which are duplicated between the znode and inode. If the generic
- * inode has the correct field it should be used, and the ZFS code
- * updated to access the inode. This can be done incrementally.
+ * Update the embedded inode given the znode.
*/
void
-zfs_inode_update(znode_t *zp)
+zfs_znode_update_vfs(znode_t *zp)
{
- zfsvfs_t *zfsvfs;
struct inode *ip;
uint32_t blksize;
u_longlong_t i_blocks;
ASSERT(zp != NULL);
- zfsvfs = ZTOZSB(zp);
ip = ZTOI(zp);
/* Skip .zfs control nodes which do not exist on disk. */
dmu_object_size_from_db(sa_get_db(zp->z_sa_hdl), &blksize, &i_blocks);
spin_lock(&ip->i_lock);
+ ip->i_mode = zp->z_mode;
ip->i_blocks = i_blocks;
i_size_write(ip, zp->z_size);
spin_unlock(&ip->i_lock);
uint64_t tmp_gen;
uint64_t links;
uint64_t z_uid, z_gid;
- uint64_t atime[2], mtime[2], ctime[2];
+ uint64_t atime[2], mtime[2], ctime[2], btime[2];
+ inode_timespec_t tmp_ts;
uint64_t projid = ZFS_DEFAULT_PROJID;
- sa_bulk_attr_t bulk[11];
+ sa_bulk_attr_t bulk[12];
int count = 0;
ASSERT(zfsvfs != NULL);
ASSERT3P(zp->z_xattr_cached, ==, NULL);
zp->z_unlinked = B_FALSE;
zp->z_atime_dirty = B_FALSE;
- zp->z_moved = B_FALSE;
+#if !defined(HAVE_FILEMAP_RANGE_HAS_PAGE)
zp->z_is_mapped = B_FALSE;
+#endif
zp->z_is_ctldir = B_FALSE;
- zp->z_is_stale = B_FALSE;
zp->z_suspended = B_FALSE;
zp->z_sa_hdl = NULL;
zp->z_mapcnt = 0;
zp->z_blksz = blksz;
zp->z_seq = 0x7A4653;
zp->z_sync_cnt = 0;
+ zp->z_sync_writes_cnt = 0;
+ zp->z_async_writes_cnt = 0;
zfs_znode_sa_init(zfsvfs, zp, db, obj_type, hdl);
SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_ATIME(zfsvfs), NULL, &atime, 16);
SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MTIME(zfsvfs), NULL, &mtime, 16);
SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zfsvfs), NULL, &ctime, 16);
+ SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CRTIME(zfsvfs), NULL, &btime, 16);
if (sa_bulk_lookup(zp->z_sa_hdl, bulk, count) != 0 || tmp_gen == 0 ||
(dmu_objset_projectquota_enabled(zfsvfs->z_os) &&
if (zp->z_pflags & ZFS_XATTR)
zp->z_xattr_parent = parent;
- ZFS_TIME_DECODE(&ip->i_atime, atime);
- ZFS_TIME_DECODE(&ip->i_mtime, mtime);
- ZFS_TIME_DECODE(&ip->i_ctime, ctime);
+ ZFS_TIME_DECODE(&tmp_ts, atime);
+ zpl_inode_set_atime_to_ts(ip, tmp_ts);
+ ZFS_TIME_DECODE(&tmp_ts, mtime);
+ zpl_inode_set_mtime_to_ts(ip, tmp_ts);
+ ZFS_TIME_DECODE(&tmp_ts, ctime);
+ zpl_inode_set_ctime_to_ts(ip, tmp_ts);
+ ZFS_TIME_DECODE(&zp->z_btime, btime);
ip->i_ino = zp->z_id;
- zfs_inode_update(zp);
+ zfs_znode_update_vfs(zp);
zfs_inode_set_ops(zfsvfs, ip);
/*
* number is already hashed for this super block. This can never
* happen because the inode numbers map 1:1 with the object numbers.
*
- * The one exception is rolling back a mounted file system, but in
- * this case all the active inode are unhashed during the rollback.
+ * Exceptions include rolling back a mounted file system, either
+ * from the zfs rollback or zfs recv command.
+ *
+ * Active inodes are unhashed during the rollback, but since zrele
+ * can happen asynchronously, we can't guarantee they've been
+ * unhashed. This can cause hash collisions in unlinked drain
+ * processing so do not hash unlinked znodes.
*/
- VERIFY3S(insert_inode_locked(ip), ==, 0);
+ if (links > 0)
+ VERIFY3S(insert_inode_locked(ip), ==, 0);
mutex_enter(&zfsvfs->z_znodes_lock);
list_insert_tail(&zfsvfs->z_all_znodes, zp);
- zfsvfs->z_nr_znodes++;
- membar_producer();
mutex_exit(&zfsvfs->z_znodes_lock);
- unlock_new_inode(ip);
+ if (links > 0)
+ unlock_new_inode(ip);
return (zp);
error:
mutex_enter(&zp->z_lock);
ASSERT3U(zp->z_id, ==, obj_num);
/*
+ * If zp->z_unlinked is set, the znode is already marked
+ * for deletion and should not be discovered. Check this
+ * after checking igrab() due to fsetxattr() & O_TMPFILE.
+ *
* If igrab() returns NULL the VFS has independently
* determined the inode should be evicted and has
* called iput_final() to start the eviction process.
* the VFS that this inode should not be evicted.
*/
if (igrab(ZTOI(zp)) == NULL) {
- mutex_exit(&zp->z_lock);
- sa_buf_rele(db, NULL);
- zfs_znode_hold_exit(zfsvfs, zh);
- /* inode might need this to finish evict */
- cond_resched();
- goto again;
+ if (zp->z_unlinked)
+ err = SET_ERROR(ENOENT);
+ else
+ err = SET_ERROR(EAGAIN);
+ } else {
+ *zpp = zp;
+ err = 0;
}
- *zpp = zp;
- err = 0;
+
mutex_exit(&zp->z_lock);
sa_buf_rele(db, NULL);
zfs_znode_hold_exit(zfsvfs, zh);
+
+ if (err == EAGAIN) {
+ /* inode might need this to finish evict */
+ cond_resched();
+ goto again;
+ }
return (err);
}
uint64_t obj_num = zp->z_id;
uint64_t mode;
uint64_t links;
- sa_bulk_attr_t bulk[10];
+ sa_bulk_attr_t bulk[11];
int err;
int count = 0;
uint64_t gen;
uint64_t z_uid, z_gid;
- uint64_t atime[2], mtime[2], ctime[2];
+ uint64_t atime[2], mtime[2], ctime[2], btime[2];
+ inode_timespec_t tmp_ts;
uint64_t projid = ZFS_DEFAULT_PROJID;
znode_hold_t *zh;
&mtime, 16);
SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zfsvfs), NULL,
&ctime, 16);
+ SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CRTIME(zfsvfs), NULL, &btime, 16);
if (sa_bulk_lookup(zp->z_sa_hdl, bulk, count)) {
zfs_znode_dmu_fini(zp);
zfs_uid_write(ZTOI(zp), z_uid);
zfs_gid_write(ZTOI(zp), z_gid);
- ZFS_TIME_DECODE(&ZTOI(zp)->i_atime, atime);
- ZFS_TIME_DECODE(&ZTOI(zp)->i_mtime, mtime);
- ZFS_TIME_DECODE(&ZTOI(zp)->i_ctime, ctime);
+ ZFS_TIME_DECODE(&tmp_ts, atime);
+ zpl_inode_set_atime_to_ts(ZTOI(zp), tmp_ts);
+ ZFS_TIME_DECODE(&tmp_ts, mtime);
+ zpl_inode_set_mtime_to_ts(ZTOI(zp), tmp_ts);
+ ZFS_TIME_DECODE(&tmp_ts, ctime);
+ zpl_inode_set_ctime_to_ts(ZTOI(zp), tmp_ts);
+ ZFS_TIME_DECODE(&zp->z_btime, btime);
if ((uint32_t)gen != ZTOI(zp)->i_generation) {
zfs_znode_dmu_fini(zp);
zp->z_blksz = doi.doi_data_block_size;
zp->z_atime_dirty = B_FALSE;
- zfs_inode_update(zp);
+ zfs_znode_update_vfs(zp);
/*
* If the file has zero links, then it has been unlinked on the send
boolean_t
zfs_relatime_need_update(const struct inode *ip)
{
- inode_timespec_t now;
+ inode_timespec_t now, tmp_atime, tmp_ts;
gethrestime(&now);
+ tmp_atime = zpl_inode_get_atime(ip);
/*
* In relatime mode, only update the atime if the previous atime
* is earlier than either the ctime or mtime or if at least a day
* has passed since the last update of atime.
*/
- if (zfs_compare_timespec(&ip->i_mtime, &ip->i_atime) >= 0)
+ tmp_ts = zpl_inode_get_mtime(ip);
+ if (zfs_compare_timespec(&tmp_ts, &tmp_atime) >= 0)
return (B_TRUE);
- if (zfs_compare_timespec(&ip->i_ctime, &ip->i_atime) >= 0)
+ tmp_ts = zpl_inode_get_ctime(ip);
+ if (zfs_compare_timespec(&tmp_ts, &tmp_atime) >= 0)
return (B_TRUE);
- if ((hrtime_t)now.tv_sec - (hrtime_t)ip->i_atime.tv_sec >= 24*60*60)
+ if ((hrtime_t)now.tv_sec - (hrtime_t)tmp_atime.tv_sec >= 24*60*60)
return (B_TRUE);
return (B_FALSE);
zfs_tstamp_update_setup(znode_t *zp, uint_t flag, uint64_t mtime[2],
uint64_t ctime[2])
{
- inode_timespec_t now;
+ inode_timespec_t now, tmp_ts;
gethrestime(&now);
if (flag & ATTR_MTIME) {
ZFS_TIME_ENCODE(&now, mtime);
- ZFS_TIME_DECODE(&(ZTOI(zp)->i_mtime), mtime);
+ ZFS_TIME_DECODE(&tmp_ts, mtime);
+ zpl_inode_set_mtime_to_ts(ZTOI(zp), tmp_ts);
if (ZTOZSB(zp)->z_use_fuids) {
zp->z_pflags |= (ZFS_ARCHIVE |
ZFS_AV_MODIFIED);
if (flag & ATTR_CTIME) {
ZFS_TIME_ENCODE(&now, ctime);
- ZFS_TIME_DECODE(&(ZTOI(zp)->i_ctime), ctime);
+ ZFS_TIME_DECODE(&tmp_ts, ctime);
+ zpl_inode_set_ctime_to_ts(ZTOI(zp), tmp_ts);
if (ZTOZSB(zp)->z_use_fuids)
zp->z_pflags |= ZFS_ARCHIVE;
}
{
zfsvfs_t *zfsvfs = ZTOZSB(zp);
dmu_tx_t *tx;
- locked_range_t *lr;
+ zfs_locked_range_t *lr;
uint64_t newblksz;
int error;
flush_dcache_page(pp);
pb = kmap(pp);
- bzero(pb + off, len);
+ memset(pb + off, 0, len);
kunmap(pp);
if (mapping_writably_mapped(mp))
zfs_free_range(znode_t *zp, uint64_t off, uint64_t len)
{
zfsvfs_t *zfsvfs = ZTOZSB(zp);
- locked_range_t *lr;
+ zfs_locked_range_t *lr;
int error;
/*
* Zero partial page cache entries. This must be done under a
* range lock in order to keep the ARC and page cache in sync.
*/
- if (zp->z_is_mapped) {
+ if (zn_has_cached_data(zp, off, off + len - 1)) {
loff_t first_page, last_page, page_len;
loff_t first_page_offset, last_page_offset;
{
zfsvfs_t *zfsvfs = ZTOZSB(zp);
dmu_tx_t *tx;
- locked_range_t *lr;
+ zfs_locked_range_t *lr;
int error;
sa_bulk_attr_t bulk[2];
int count = 0;
dmu_tx_commit(tx);
- zfs_inode_update(zp);
+ zfs_znode_update_vfs(zp);
error = 0;
out:
while ((elem = nvlist_next_nvpair(zplprops, elem)) != NULL) {
/* For the moment we expect all zpl props to be uint64_ts */
uint64_t val;
- char *name;
+ const char *name;
ASSERT(nvpair_type(elem) == DATA_TYPE_UINT64);
VERIFY(nvpair_value_uint64(elem, &val) == 0);
}
ASSERT(version != 0);
error = zap_update(os, moid, ZPL_VERSION_STR, 8, 1, &version, tx);
+ ASSERT(error == 0);
/*
* Create zap object used for SA attribute registration
rootzp = kmem_cache_alloc(znode_cache, KM_SLEEP);
rootzp->z_unlinked = B_FALSE;
rootzp->z_atime_dirty = B_FALSE;
- rootzp->z_moved = B_FALSE;
rootzp->z_is_sa = USE_SA(version, os);
rootzp->z_pflags = 0;
}
VERIFY(0 == zfs_acl_ids_create(rootzp, IS_ROOT_NODE, &vattr,
- cr, NULL, &acl_ids));
+ cr, NULL, &acl_ids, zfs_init_idmap));
zfs_mknode(rootzp, &vattr, tx, cr, IS_ROOT_NODE, &zp, &acl_ids);
ASSERT3P(zp, ==, rootzp);
error = zap_add(os, moid, ZFS_ROOT_OBJ, 8, 1, &rootzp->z_id, tx);
static int
zfs_grab_sa_handle(objset_t *osp, uint64_t obj, sa_handle_t **hdlp,
- dmu_buf_t **db, void *tag)
+ dmu_buf_t **db, const void *tag)
{
dmu_object_info_t doi;
int error;
return (0);
}
-void
-zfs_release_sa_handle(sa_handle_t *hdl, dmu_buf_t *db, void *tag)
+static void
+zfs_release_sa_handle(sa_handle_t *hdl, dmu_buf_t *db, const void *tag)
{
sa_handle_destroy(hdl);
sa_buf_rele(db, tag);
} else if (error != ENOENT) {
return (error);
}
- error = 0;
for (;;) {
uint64_t pobj = 0;
size_t complen;
int is_xattrdir = 0;
- if (prevdb)
+ if (prevdb) {
+ ASSERT(prevhdl != NULL);
zfs_release_sa_handle(prevhdl, prevdb, FTAG);
+ }
if ((error = zfs_obj_to_pobj(osp, sa_hdl, sa_table, &pobj,
&is_xattrdir)) != 0)
component[0] = '/';
if (is_xattrdir) {
- (void) sprintf(component + 1, "<xattrdir>");
+ strcpy(component + 1, "<xattrdir>");
} else {
error = zap_value_search(osp, pobj, obj,
ZFS_DIRENT_OBJ(-1ULL), component + 1);
complen = strlen(component);
path -= complen;
ASSERT(path >= buf);
- bcopy(component, path, complen);
+ memcpy(path, component, complen);
obj = pobj;
if (sa_hdl != hdl) {
return (error);
}
+/*
+ * Read a property stored within the master node.
+ */
+int
+zfs_get_zplprop(objset_t *os, zfs_prop_t prop, uint64_t *value)
+{
+ uint64_t *cached_copy = NULL;
+
+ /*
+ * Figure out where in the objset_t the cached copy would live, if it
+ * is available for the requested property.
+ */
+ if (os != NULL) {
+ switch (prop) {
+ case ZFS_PROP_VERSION:
+ cached_copy = &os->os_version;
+ break;
+ case ZFS_PROP_NORMALIZE:
+ cached_copy = &os->os_normalization;
+ break;
+ case ZFS_PROP_UTF8ONLY:
+ cached_copy = &os->os_utf8only;
+ break;
+ case ZFS_PROP_CASE:
+ cached_copy = &os->os_casesensitivity;
+ break;
+ default:
+ break;
+ }
+ }
+ if (cached_copy != NULL && *cached_copy != OBJSET_PROP_UNINITIALIZED) {
+ *value = *cached_copy;
+ return (0);
+ }
+
+ /*
+ * If the property wasn't cached, look up the file system's value for
+ * the property. For the version property, we look up a slightly
+ * different string.
+ */
+ const char *pname;
+ int error = ENOENT;
+ if (prop == ZFS_PROP_VERSION)
+ pname = ZPL_VERSION_STR;
+ else
+ pname = zfs_prop_to_name(prop);
+
+ if (os != NULL) {
+ ASSERT3U(os->os_phys->os_type, ==, DMU_OST_ZFS);
+ error = zap_lookup(os, MASTER_NODE_OBJ, pname, 8, 1, value);
+ }
+
+ if (error == ENOENT) {
+ /* No value set, use the default value */
+ switch (prop) {
+ case ZFS_PROP_VERSION:
+ *value = ZPL_VERSION;
+ break;
+ case ZFS_PROP_NORMALIZE:
+ case ZFS_PROP_UTF8ONLY:
+ *value = 0;
+ break;
+ case ZFS_PROP_CASE:
+ *value = ZFS_CASE_SENSITIVE;
+ break;
+ case ZFS_PROP_ACLTYPE:
+ *value = ZFS_ACLTYPE_OFF;
+ break;
+ default:
+ return (error);
+ }
+ error = 0;
+ }
+
+ /*
+ * If one of the methods for getting the property value above worked,
+ * copy it into the objset_t's cache.
+ */
+ if (error == 0 && cached_copy != NULL) {
+ *cached_copy = *value;
+ }
+
+ return (error);
+}
+
#if defined(_KERNEL)
EXPORT_SYMBOL(zfs_create_fs);
EXPORT_SYMBOL(zfs_obj_to_path);