// SPDX-License-Identifier: CDDL-1.0 /* * 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 * https://opensource.org/license/CDDL-1.0. */ /* * Copyright (c) 2011, Lawrence Livermore National Security, LLC. * Copyright (c) 2015 by Chunwei Chen. All rights reserved. * Copyright (c) 2025, Rob Norris * Copyright (c) 2026, TrueNAS. */ #include #include #include #include #include #include #include #include #include #include static struct dentry * zpl_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { cred_t *cr = CRED(); struct inode *ip; znode_t *zp; int error; fstrans_cookie_t cookie; pathname_t *ppn = NULL; pathname_t pn; int zfs_flags = 0; zfsvfs_t *zfsvfs = dentry->d_sb->s_fs_info; dsl_dataset_t *ds = dmu_objset_ds(zfsvfs->z_os); size_t dlen = dlen(dentry); /* * If z_longname is disabled, disallow create or rename of names * longer than ZAP_MAXNAMELEN. * * This is needed in cases where longname was enabled first and some * files/dirs with names > ZAP_MAXNAMELEN were created. And later * longname was disabled. In such a case allow access to existing * longnames. But disallow creation newer longnamed entities. */ if (!zfsvfs->z_longname && (dlen >= ZAP_MAXNAMELEN)) { /* * If this is for create or rename fail it. */ if (!dsl_dataset_feature_is_active(ds, SPA_FEATURE_LONGNAME) || (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET))) return (ERR_PTR(-ENAMETOOLONG)); } if (dlen >= ZAP_MAXNAMELEN_NEW) { return (ERR_PTR(-ENAMETOOLONG)); } crhold(cr); cookie = spl_fstrans_mark(); /* If we are a case insensitive fs, we need the real name */ if (zfsvfs->z_case == ZFS_CASE_INSENSITIVE) { zfs_flags = FIGNORECASE; pn_alloc(&pn); ppn = &pn; } error = -zfs_lookup(ITOZ(dir), dname(dentry), &zp, zfs_flags, cr, NULL, ppn); spl_fstrans_unmark(cookie); ASSERT3S(error, <=, 0); crfree(cr); spin_lock(&dentry->d_lock); dentry->d_time = jiffies; spin_unlock(&dentry->d_lock); if (error) { /* * If we have a case sensitive fs, we do not want to * insert negative entries, so return NULL for ENOENT. * Fall through if the error is not ENOENT. Also free memory. */ if (ppn) { pn_free(ppn); if (error == -ENOENT) return (NULL); } if (error == -ENOENT) return (d_splice_alias(NULL, dentry)); else return (ERR_PTR(error)); } ip = ZTOI(zp); /* * If we are case insensitive, call the correct function * to install the name. */ if (ppn) { struct dentry *new_dentry; struct qstr ci_name; if (strcmp(dname(dentry), pn.pn_buf) == 0) { new_dentry = d_splice_alias(ip, dentry); } else { ci_name.name = pn.pn_buf; ci_name.len = strlen(pn.pn_buf); new_dentry = d_add_ci(dentry, ip, &ci_name); } pn_free(ppn); return (new_dentry); } else { return (d_splice_alias(ip, dentry)); } } void zpl_vap_init(vattr_t *vap, struct inode *dir, umode_t mode, cred_t *cr, zidmap_t *idmap) { vap->va_mask = ATTR_MODE; vap->va_mode = mode; vap->va_uid = zfs_vfsuid_to_uid(idmap, zfs_i_user_ns(dir), crgetuid(cr)); if (dir->i_mode & S_ISGID) { vap->va_gid = KGID_TO_SGID(dir->i_gid); if (S_ISDIR(mode)) vap->va_mode |= S_ISGID; } else { vap->va_gid = zfs_vfsgid_to_gid(idmap, zfs_i_user_ns(dir), crgetgid(cr)); } } static inline bool is_nametoolong(struct dentry *dentry) { zfsvfs_t *zfsvfs = dentry->d_sb->s_fs_info; size_t dlen = dlen(dentry); return ((!zfsvfs->z_longname && dlen >= ZAP_MAXNAMELEN) || dlen >= ZAP_MAXNAMELEN_NEW); } #if defined(HAVE_IOPS_CREATE_NO_FLAG_ARG) ZPL_IDMAP_IOP_DEFINE(int, zpl_create, 3, struct inode *, dir, struct dentry *, dentry, umode_t, mode) #else ZPL_IDMAP_IOP_DEFINE(int, zpl_create, 4, struct inode *, dir, struct dentry *, dentry, umode_t, mode, bool, flag) #endif { cred_t *cr = CRED(); znode_t *zp; vattr_t *vap; int error; fstrans_cookie_t cookie; if (is_nametoolong(dentry)) { return (-ENAMETOOLONG); } crhold(cr); vap = kmem_zalloc(sizeof (vattr_t), KM_SLEEP); zpl_vap_init(vap, dir, mode, cr, idmap); cookie = spl_fstrans_mark(); error = -zfs_create_idmap(ITOZ(dir), dname(dentry), vap, 0, mode, &zp, cr, 0, NULL, idmap); if (error == 0) { error = zpl_xattr_security_init(ZTOI(zp), dir, &dentry->d_name); if (error == 0) error = zpl_init_acl(ZTOI(zp), dir); if (error) { (void) zfs_remove(ITOZ(dir), dname(dentry), cr, 0); remove_inode_hash(ZTOI(zp)); iput(ZTOI(zp)); } else { d_instantiate(dentry, ZTOI(zp)); } } spl_fstrans_unmark(cookie); kmem_free(vap, sizeof (vattr_t)); crfree(cr); ASSERT3S(error, <=, 0); return (error); } ZPL_IDMAP_IOP_DEFINE(int, zpl_mknod, 4, struct inode *, dir, struct dentry *, dentry, umode_t, mode, dev_t, rdev) { cred_t *cr = CRED(); znode_t *zp; vattr_t *vap; int error; fstrans_cookie_t cookie; if (is_nametoolong(dentry)) { return (-ENAMETOOLONG); } /* * We currently expect Linux to supply rdev=0 for all sockets * and fifos, but we want to know if this behavior ever changes. */ if (S_ISSOCK(mode) || S_ISFIFO(mode)) ASSERT0(rdev); crhold(cr); vap = kmem_zalloc(sizeof (vattr_t), KM_SLEEP); zpl_vap_init(vap, dir, mode, cr, idmap); vap->va_rdev = rdev; cookie = spl_fstrans_mark(); error = -zfs_create_idmap(ITOZ(dir), dname(dentry), vap, 0, mode, &zp, cr, 0, NULL, idmap); if (error == 0) { error = zpl_xattr_security_init(ZTOI(zp), dir, &dentry->d_name); if (error == 0) error = zpl_init_acl(ZTOI(zp), dir); if (error) { (void) zfs_remove(ITOZ(dir), dname(dentry), cr, 0); remove_inode_hash(ZTOI(zp)); iput(ZTOI(zp)); } else { d_instantiate(dentry, ZTOI(zp)); } } spl_fstrans_unmark(cookie); kmem_free(vap, sizeof (vattr_t)); crfree(cr); ASSERT3S(error, <=, 0); return (error); } #if defined(HAVE_TMPFILE_FILE) ZPL_IDMAP_IOP_DEFINE(int, zpl_tmpfile, 3, struct inode *, dir, struct file *, file, umode_t, mode) #else ZPL_IDMAP_IOP_DEFINE(int, zpl_tmpfile, 3, struct inode *, dir, struct dentry *, dentry, umode_t, mode) #endif { cred_t *cr = CRED(); struct inode *ip; vattr_t *vap; int error; fstrans_cookie_t cookie; crhold(cr); vap = kmem_zalloc(sizeof (vattr_t), KM_SLEEP); /* * The VFS does not apply the umask, therefore it is applied here * when POSIX ACLs are not enabled. */ if (!IS_POSIXACL(dir)) mode &= ~current_umask(); zpl_vap_init(vap, dir, mode, cr, idmap); cookie = spl_fstrans_mark(); error = -zfs_tmpfile_idmap(dir, vap, 0, mode, &ip, cr, 0, NULL, idmap); if (error == 0) { /* d_tmpfile will do drop_nlink, so we should set it first */ set_nlink(ip, 1); #ifdef HAVE_TMPFILE_FILE d_tmpfile(file, ip); error = zpl_xattr_security_init(ip, dir, &file->f_path.dentry->d_name); #else d_tmpfile(dentry, ip); error = zpl_xattr_security_init(ip, dir, &dentry->d_name); #endif if (error == 0) error = zpl_init_acl(ip, dir); #ifdef HAVE_TMPFILE_FILE error = finish_open_simple(file, error); #endif /* * don't need to handle error here, file is already in * unlinked set. */ } spl_fstrans_unmark(cookie); kmem_free(vap, sizeof (vattr_t)); crfree(cr); ASSERT3S(error, <=, 0); return (error); } static int zpl_unlink(struct inode *dir, struct dentry *dentry) { cred_t *cr = CRED(); int error; fstrans_cookie_t cookie; zfsvfs_t *zfsvfs = dentry->d_sb->s_fs_info; crhold(cr); cookie = spl_fstrans_mark(); error = -zfs_remove(ITOZ(dir), dname(dentry), cr, 0); /* * For a CI FS we must invalidate the dentry to prevent the * creation of negative entries. */ if (error == 0 && zfsvfs->z_case == ZFS_CASE_INSENSITIVE) d_invalidate(dentry); spl_fstrans_unmark(cookie); crfree(cr); ASSERT3S(error, <=, 0); return (error); } #if defined(HAVE_MKDIR_DENTRY_RETURN) ZPL_IDMAP_IOP_DEFINE(struct dentry *, zpl_mkdir, 3, struct inode *, dir, struct dentry *, dentry, umode_t, mode) #else ZPL_IDMAP_IOP_DEFINE(int, zpl_mkdir, 3, struct inode *, dir, struct dentry *, dentry, umode_t, mode) #endif { cred_t *cr = CRED(); vattr_t *vap; znode_t *zp; int error; fstrans_cookie_t cookie; if (is_nametoolong(dentry)) { error = -ENAMETOOLONG; goto err; } crhold(cr); vap = kmem_zalloc(sizeof (vattr_t), KM_SLEEP); zpl_vap_init(vap, dir, mode | S_IFDIR, cr, idmap); cookie = spl_fstrans_mark(); error = -zfs_mkdir_idmap(ITOZ(dir), dname(dentry), vap, &zp, cr, 0, NULL, idmap); if (error == 0) { error = zpl_xattr_security_init(ZTOI(zp), dir, &dentry->d_name); if (error == 0) error = zpl_init_acl(ZTOI(zp), dir); if (error) { (void) zfs_rmdir(ITOZ(dir), dname(dentry), NULL, cr, 0); remove_inode_hash(ZTOI(zp)); iput(ZTOI(zp)); } else { d_instantiate(dentry, ZTOI(zp)); } } spl_fstrans_unmark(cookie); kmem_free(vap, sizeof (vattr_t)); crfree(cr); err: ASSERT3S(error, <=, 0); #if defined(HAVE_MKDIR_DENTRY_RETURN) return (error != 0 ? ERR_PTR(error) : NULL); #else return (error); #endif } static int zpl_rmdir(struct inode *dir, struct dentry *dentry) { cred_t *cr = CRED(); int error; fstrans_cookie_t cookie; zfsvfs_t *zfsvfs = dentry->d_sb->s_fs_info; crhold(cr); cookie = spl_fstrans_mark(); error = -zfs_rmdir(ITOZ(dir), dname(dentry), NULL, cr, 0); /* * For a CI FS we must invalidate the dentry to prevent the * creation of negative entries. */ if (error == 0 && zfsvfs->z_case == ZFS_CASE_INSENSITIVE) d_invalidate(dentry); spl_fstrans_unmark(cookie); crfree(cr); ASSERT3S(error, <=, 0); return (error); } ZPL_IDMAP_IOP_DEFINE(int, zpl_getattr, 4, const struct path *, path, struct kstat *, stat, u32, request_mask, unsigned int, query_flags) { int error; fstrans_cookie_t cookie; struct inode *ip = path->dentry->d_inode; znode_t *zp __maybe_unused = ITOZ(ip); cookie = spl_fstrans_mark(); /* * XXX query_flags currently ignored. */ error = -zfs_getattr_fast(idmap, request_mask, ip, stat); #ifdef STATX_CHANGE_COOKIE if (request_mask & STATX_CHANGE_COOKIE) { /* * knfsd uses the STATX_CHANGE_COOKIE to surface to clients * change_info4 data, which is used to implement NFS client * name caching (see RFC 8881 Section 10.8). This number * should always increase with changes and should not be * reused. We cannot simply present ctime here because * ZFS uses a coarse timer to set them, which may cause * clients to fail to detect changes and invalidate cache. * * z_seq is a per-file 64-bit counter bumped on every change * and persisted across znode eviction, so it is presented * directly as a monotonic change cookie. Files that predate * persistence are seeded from ctime on load so the cookie * never moves backward across the upgrade. * * STATX_ATTR_CHANGE_MONOTONIC is advertised * to prevent knfsd from generating the change cookie * based on ctime. C.f. nfsd4_change_attribute in * fs/nfsd/nfsfh.c. */ stat->change_cookie = atomic_load_64(&zp->z_seq); stat->attributes |= STATX_ATTR_CHANGE_MONOTONIC; stat->result_mask |= STATX_CHANGE_COOKIE; } #endif #ifdef STATX_DIOALIGN if (request_mask & STATX_DIOALIGN) { uint64_t align; if (zfs_get_direct_alignment(zp, &align) == 0) { stat->dio_mem_align = PAGE_SIZE; stat->dio_offset_align = align; stat->result_mask |= STATX_DIOALIGN; } } #endif #ifdef STATX_ATTR_IMMUTABLE if (zp->z_pflags & ZFS_IMMUTABLE) stat->attributes |= STATX_ATTR_IMMUTABLE; stat->attributes_mask |= STATX_ATTR_IMMUTABLE; #endif #ifdef STATX_ATTR_APPEND if (zp->z_pflags & ZFS_APPENDONLY) stat->attributes |= STATX_ATTR_APPEND; stat->attributes_mask |= STATX_ATTR_APPEND; #endif #ifdef STATX_ATTR_NODUMP if (zp->z_pflags & ZFS_NODUMP) stat->attributes |= STATX_ATTR_NODUMP; stat->attributes_mask |= STATX_ATTR_NODUMP; #endif spl_fstrans_unmark(cookie); ASSERT3S(error, <=, 0); return (error); } ZPL_IDMAP_IOP_DEFINE(int, zpl_setattr, 2, struct dentry *, dentry, struct iattr *, ia) { struct inode *ip = dentry->d_inode; cred_t *cr = CRED(); vattr_t *vap; int error; fstrans_cookie_t cookie; error = zpl_setattr_prepare(idmap, dentry, ia); if (error) return (error); crhold(cr); vap = kmem_zalloc(sizeof (vattr_t), KM_SLEEP); vap->va_mask = ia->ia_valid & ATTR_IATTR_MASK; vap->va_mode = ia->ia_mode; if (ia->ia_valid & ATTR_UID) #ifdef HAVE_IATTR_VFSID vap->va_uid = zfs_vfsuid_to_uid(idmap, zfs_i_user_ns(ip), __vfsuid_val(ia->ia_vfsuid)); #else vap->va_uid = KUID_TO_SUID(ia->ia_uid); #endif if (ia->ia_valid & ATTR_GID) #ifdef HAVE_IATTR_VFSID vap->va_gid = zfs_vfsgid_to_gid(idmap, zfs_i_user_ns(ip), __vfsgid_val(ia->ia_vfsgid)); #else vap->va_gid = KGID_TO_SGID(ia->ia_gid); #endif vap->va_size = ia->ia_size; vap->va_atime = ia->ia_atime; vap->va_mtime = ia->ia_mtime; vap->va_ctime = ia->ia_ctime; if (vap->va_mask & ATTR_ATIME) zpl_inode_set_atime_to_ts(ip, zpl_inode_timestamp_truncate(ia->ia_atime, ip)); cookie = spl_fstrans_mark(); error = -zfs_setattr_idmap(ITOZ(ip), vap, 0, cr, idmap); if (!error && (ia->ia_valid & ATTR_MODE)) error = zpl_chmod_acl(ip); spl_fstrans_unmark(cookie); kmem_free(vap, sizeof (vattr_t)); crfree(cr); ASSERT3S(error, <=, 0); return (error); } ZPL_IDMAP_IOP_DEFINE(int, zpl_rename, 5, struct inode *, sdip, struct dentry *, sdentry, struct inode *, tdip, struct dentry *, tdentry, unsigned int, rflags) { cred_t *cr = CRED(); vattr_t *wo_vap = NULL; int error; fstrans_cookie_t cookie; if (is_nametoolong(tdentry)) { return (-ENAMETOOLONG); } crhold(cr); if (rflags & RENAME_WHITEOUT) { wo_vap = kmem_zalloc(sizeof (vattr_t), KM_SLEEP); zpl_vap_init(wo_vap, sdip, S_IFCHR, cr, idmap); wo_vap->va_rdev = makedevice(0, 0); } cookie = spl_fstrans_mark(); error = -zfs_rename_idmap(ITOZ(sdip), dname(sdentry), ITOZ(tdip), dname(tdentry), cr, 0, rflags, wo_vap, idmap); spl_fstrans_unmark(cookie); if (wo_vap) kmem_free(wo_vap, sizeof (vattr_t)); crfree(cr); ASSERT3S(error, <=, 0); return (error); } ZPL_IDMAP_IOP_DEFINE(int, zpl_symlink, 3, struct inode *, dir, struct dentry *, dentry, const char *, name) { cred_t *cr = CRED(); vattr_t *vap; znode_t *zp; int error; fstrans_cookie_t cookie; if (is_nametoolong(dentry)) { return (-ENAMETOOLONG); } crhold(cr); vap = kmem_zalloc(sizeof (vattr_t), KM_SLEEP); zpl_vap_init(vap, dir, S_IFLNK | S_IRWXUGO, cr, idmap); cookie = spl_fstrans_mark(); error = -zfs_symlink_idmap(ITOZ(dir), dname(dentry), vap, (char *)name, &zp, cr, 0, idmap); if (error == 0) { error = zpl_xattr_security_init(ZTOI(zp), dir, &dentry->d_name); if (error) { (void) zfs_remove(ITOZ(dir), dname(dentry), cr, 0); remove_inode_hash(ZTOI(zp)); iput(ZTOI(zp)); } else { d_instantiate(dentry, ZTOI(zp)); } } spl_fstrans_unmark(cookie); kmem_free(vap, sizeof (vattr_t)); crfree(cr); ASSERT3S(error, <=, 0); return (error); } static void zpl_put_link(void *ptr) { kmem_free(ptr, MAXPATHLEN); } static int zpl_get_link_common(struct dentry *dentry, struct inode *ip, char **link) { fstrans_cookie_t cookie; cred_t *cr = CRED(); int error; crhold(cr); *link = NULL; struct iovec iov; iov.iov_len = MAXPATHLEN; iov.iov_base = kmem_zalloc(MAXPATHLEN, KM_SLEEP); zfs_uio_t uio; zfs_uio_iovec_init(&uio, &iov, 1, 0, UIO_SYSSPACE, MAXPATHLEN - 1, 0); cookie = spl_fstrans_mark(); error = -zfs_readlink(ip, &uio, cr); spl_fstrans_unmark(cookie); crfree(cr); if (error) kmem_free(iov.iov_base, MAXPATHLEN); else *link = iov.iov_base; return (error); } static const char * zpl_get_link(struct dentry *dentry, struct inode *inode, struct delayed_call *done) { char *link = NULL; int error; if (!dentry) return (ERR_PTR(-ECHILD)); error = zpl_get_link_common(dentry, inode, &link); if (error) return (ERR_PTR(error)); set_delayed_call(done, zpl_put_link, link); return (link); } static int zpl_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) { cred_t *cr = CRED(); struct inode *ip = old_dentry->d_inode; int error; fstrans_cookie_t cookie; if (is_nametoolong(dentry)) { return (-ENAMETOOLONG); } if (ip->i_nlink >= ZFS_LINK_MAX) return (-EMLINK); crhold(cr); zpl_inode_set_ctime_to_ts(ip, current_time(ip)); /* Must have an existing ref, so igrab() cannot return NULL */ VERIFY3P(igrab(ip), !=, NULL); cookie = spl_fstrans_mark(); error = -zfs_link(ITOZ(dir), ITOZ(ip), dname(dentry), cr, 0); if (error) { iput(ip); goto out; } d_instantiate(dentry, ip); out: spl_fstrans_unmark(cookie); crfree(cr); ASSERT3S(error, <=, 0); return (error); } #if defined(CONFIG_FS_POSIX_ACL) #if defined(HAVE_SET_ACL_DENTRY) ZPL_IDMAP_IOP_DEFINE(int, zpl_set_acl, 3, struct dentry *, dentry, struct posix_acl *, acl, int, type) { return (zpl_set_posix_acl(d_inode(dentry), acl, type)); } #else ZPL_IDMAP_IOP_DEFINE(int, zpl_set_acl, 3, struct inode *, ip, struct posix_acl *, acl, int, type) { return (zpl_set_posix_acl(ip, acl, type)); } #endif #if defined(HAVE_GET_INODE_ACL) || defined(HAVE_GET_ACL_RCU) static struct posix_acl * zpl_get_acl(struct inode *ip, int type, bool rcu) { if (rcu) return (ERR_PTR(-ECHILD)); return (zpl_get_posix_acl(ip, type)); } #else static struct posix_acl * zpl_get_acl(struct inode *ip, int type) { return (zpl_get_posix_acl(ip, type)); } #endif #endif const struct inode_operations zpl_inode_operations = { .setattr = zpl_setattr, .getattr = zpl_getattr, .listxattr = zpl_xattr_list, #if defined(CONFIG_FS_POSIX_ACL) .set_acl = zpl_set_acl, #if defined(HAVE_GET_INODE_ACL) .get_inode_acl = zpl_get_acl, #else .get_acl = zpl_get_acl, #endif /* HAVE_GET_INODE_ACL */ #endif /* CONFIG_FS_POSIX_ACL */ }; const struct inode_operations zpl_dir_inode_operations = { .create = zpl_create, .lookup = zpl_lookup, .link = zpl_link, .unlink = zpl_unlink, .symlink = zpl_symlink, .mkdir = zpl_mkdir, .rmdir = zpl_rmdir, .mknod = zpl_mknod, .rename = zpl_rename, .tmpfile = zpl_tmpfile, .setattr = zpl_setattr, .getattr = zpl_getattr, .listxattr = zpl_xattr_list, #if defined(CONFIG_FS_POSIX_ACL) .set_acl = zpl_set_acl, #if defined(HAVE_GET_INODE_ACL) .get_inode_acl = zpl_get_acl, #else .get_acl = zpl_get_acl, #endif /* HAVE_GET_INODE_ACL */ #endif /* CONFIG_FS_POSIX_ACL */ }; const struct inode_operations zpl_symlink_inode_operations = { .get_link = zpl_get_link, .setattr = zpl_setattr, .getattr = zpl_getattr, .listxattr = zpl_xattr_list, }; const struct inode_operations zpl_special_inode_operations = { .setattr = zpl_setattr, .getattr = zpl_getattr, .listxattr = zpl_xattr_list, #if defined(CONFIG_FS_POSIX_ACL) .set_acl = zpl_set_acl, #if defined(HAVE_GET_INODE_ACL) .get_inode_acl = zpl_get_acl, #else .get_acl = zpl_get_acl, #endif /* HAVE_GET_INODE_ACL */ #endif /* CONFIG_FS_POSIX_ACL */ };