162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org> 462306a36Sopenharmony_ci * Copyright (C) 2018 Samsung Electronics Co., Ltd. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/kernel.h> 862306a36Sopenharmony_ci#include <linux/fs.h> 962306a36Sopenharmony_ci#include <linux/filelock.h> 1062306a36Sopenharmony_ci#include <linux/uaccess.h> 1162306a36Sopenharmony_ci#include <linux/backing-dev.h> 1262306a36Sopenharmony_ci#include <linux/writeback.h> 1362306a36Sopenharmony_ci#include <linux/xattr.h> 1462306a36Sopenharmony_ci#include <linux/falloc.h> 1562306a36Sopenharmony_ci#include <linux/fsnotify.h> 1662306a36Sopenharmony_ci#include <linux/dcache.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/vmalloc.h> 1962306a36Sopenharmony_ci#include <linux/sched/xacct.h> 2062306a36Sopenharmony_ci#include <linux/crc32c.h> 2162306a36Sopenharmony_ci#include <linux/namei.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "glob.h" 2462306a36Sopenharmony_ci#include "oplock.h" 2562306a36Sopenharmony_ci#include "connection.h" 2662306a36Sopenharmony_ci#include "vfs.h" 2762306a36Sopenharmony_ci#include "vfs_cache.h" 2862306a36Sopenharmony_ci#include "smbacl.h" 2962306a36Sopenharmony_ci#include "ndr.h" 3062306a36Sopenharmony_ci#include "auth.h" 3162306a36Sopenharmony_ci#include "misc.h" 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include "smb_common.h" 3462306a36Sopenharmony_ci#include "mgmt/share_config.h" 3562306a36Sopenharmony_ci#include "mgmt/tree_connect.h" 3662306a36Sopenharmony_ci#include "mgmt/user_session.h" 3762306a36Sopenharmony_ci#include "mgmt/user_config.h" 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic void ksmbd_vfs_inherit_owner(struct ksmbd_work *work, 4062306a36Sopenharmony_ci struct inode *parent_inode, 4162306a36Sopenharmony_ci struct inode *inode) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci if (!test_share_config_flag(work->tcon->share_conf, 4462306a36Sopenharmony_ci KSMBD_SHARE_FLAG_INHERIT_OWNER)) 4562306a36Sopenharmony_ci return; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci i_uid_write(inode, i_uid_read(parent_inode)); 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/** 5162306a36Sopenharmony_ci * ksmbd_vfs_lock_parent() - lock parent dentry if it is stable 5262306a36Sopenharmony_ci */ 5362306a36Sopenharmony_ciint ksmbd_vfs_lock_parent(struct dentry *parent, struct dentry *child) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci inode_lock_nested(d_inode(parent), I_MUTEX_PARENT); 5662306a36Sopenharmony_ci if (child->d_parent != parent) { 5762306a36Sopenharmony_ci inode_unlock(d_inode(parent)); 5862306a36Sopenharmony_ci return -ENOENT; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci return 0; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config *share_conf, 6562306a36Sopenharmony_ci char *pathname, unsigned int flags, 6662306a36Sopenharmony_ci struct path *parent_path, 6762306a36Sopenharmony_ci struct path *path) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci struct qstr last; 7062306a36Sopenharmony_ci struct filename *filename; 7162306a36Sopenharmony_ci struct path *root_share_path = &share_conf->vfs_path; 7262306a36Sopenharmony_ci int err, type; 7362306a36Sopenharmony_ci struct dentry *d; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (pathname[0] == '\0') { 7662306a36Sopenharmony_ci pathname = share_conf->path; 7762306a36Sopenharmony_ci root_share_path = NULL; 7862306a36Sopenharmony_ci } else { 7962306a36Sopenharmony_ci flags |= LOOKUP_BENEATH; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci filename = getname_kernel(pathname); 8362306a36Sopenharmony_ci if (IS_ERR(filename)) 8462306a36Sopenharmony_ci return PTR_ERR(filename); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci err = vfs_path_parent_lookup(filename, flags, 8762306a36Sopenharmony_ci parent_path, &last, &type, 8862306a36Sopenharmony_ci root_share_path); 8962306a36Sopenharmony_ci if (err) { 9062306a36Sopenharmony_ci putname(filename); 9162306a36Sopenharmony_ci return err; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (unlikely(type != LAST_NORM)) { 9562306a36Sopenharmony_ci path_put(parent_path); 9662306a36Sopenharmony_ci putname(filename); 9762306a36Sopenharmony_ci return -ENOENT; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci err = mnt_want_write(parent_path->mnt); 10162306a36Sopenharmony_ci if (err) { 10262306a36Sopenharmony_ci path_put(parent_path); 10362306a36Sopenharmony_ci putname(filename); 10462306a36Sopenharmony_ci return -ENOENT; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci inode_lock_nested(parent_path->dentry->d_inode, I_MUTEX_PARENT); 10862306a36Sopenharmony_ci d = lookup_one_qstr_excl(&last, parent_path->dentry, 0); 10962306a36Sopenharmony_ci if (IS_ERR(d)) 11062306a36Sopenharmony_ci goto err_out; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (d_is_negative(d)) { 11362306a36Sopenharmony_ci dput(d); 11462306a36Sopenharmony_ci goto err_out; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci path->dentry = d; 11862306a36Sopenharmony_ci path->mnt = mntget(parent_path->mnt); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (test_share_config_flag(share_conf, KSMBD_SHARE_FLAG_CROSSMNT)) { 12162306a36Sopenharmony_ci err = follow_down(path, 0); 12262306a36Sopenharmony_ci if (err < 0) { 12362306a36Sopenharmony_ci path_put(path); 12462306a36Sopenharmony_ci goto err_out; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci putname(filename); 12962306a36Sopenharmony_ci return 0; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cierr_out: 13262306a36Sopenharmony_ci inode_unlock(d_inode(parent_path->dentry)); 13362306a36Sopenharmony_ci mnt_drop_write(parent_path->mnt); 13462306a36Sopenharmony_ci path_put(parent_path); 13562306a36Sopenharmony_ci putname(filename); 13662306a36Sopenharmony_ci return -ENOENT; 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_civoid ksmbd_vfs_query_maximal_access(struct mnt_idmap *idmap, 14062306a36Sopenharmony_ci struct dentry *dentry, __le32 *daccess) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci *daccess = cpu_to_le32(FILE_READ_ATTRIBUTES | READ_CONTROL); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci if (!inode_permission(idmap, d_inode(dentry), MAY_OPEN | MAY_WRITE)) 14562306a36Sopenharmony_ci *daccess |= cpu_to_le32(WRITE_DAC | WRITE_OWNER | SYNCHRONIZE | 14662306a36Sopenharmony_ci FILE_WRITE_DATA | FILE_APPEND_DATA | 14762306a36Sopenharmony_ci FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | 14862306a36Sopenharmony_ci FILE_DELETE_CHILD); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (!inode_permission(idmap, d_inode(dentry), MAY_OPEN | MAY_READ)) 15162306a36Sopenharmony_ci *daccess |= FILE_READ_DATA_LE | FILE_READ_EA_LE; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (!inode_permission(idmap, d_inode(dentry), MAY_OPEN | MAY_EXEC)) 15462306a36Sopenharmony_ci *daccess |= FILE_EXECUTE_LE; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (!inode_permission(idmap, d_inode(dentry->d_parent), MAY_EXEC | MAY_WRITE)) 15762306a36Sopenharmony_ci *daccess |= FILE_DELETE_LE; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci/** 16162306a36Sopenharmony_ci * ksmbd_vfs_create() - vfs helper for smb create file 16262306a36Sopenharmony_ci * @work: work 16362306a36Sopenharmony_ci * @name: file name that is relative to share 16462306a36Sopenharmony_ci * @mode: file create mode 16562306a36Sopenharmony_ci * 16662306a36Sopenharmony_ci * Return: 0 on success, otherwise error 16762306a36Sopenharmony_ci */ 16862306a36Sopenharmony_ciint ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci struct path path; 17162306a36Sopenharmony_ci struct dentry *dentry; 17262306a36Sopenharmony_ci int err; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci dentry = ksmbd_vfs_kern_path_create(work, name, 17562306a36Sopenharmony_ci LOOKUP_NO_SYMLINKS, &path); 17662306a36Sopenharmony_ci if (IS_ERR(dentry)) { 17762306a36Sopenharmony_ci err = PTR_ERR(dentry); 17862306a36Sopenharmony_ci if (err != -ENOENT) 17962306a36Sopenharmony_ci pr_err("path create failed for %s, err %d\n", 18062306a36Sopenharmony_ci name, err); 18162306a36Sopenharmony_ci return err; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci mode |= S_IFREG; 18562306a36Sopenharmony_ci err = vfs_create(mnt_idmap(path.mnt), d_inode(path.dentry), 18662306a36Sopenharmony_ci dentry, mode, true); 18762306a36Sopenharmony_ci if (!err) { 18862306a36Sopenharmony_ci ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), 18962306a36Sopenharmony_ci d_inode(dentry)); 19062306a36Sopenharmony_ci } else { 19162306a36Sopenharmony_ci pr_err("File(%s): creation failed (err:%d)\n", name, err); 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci done_path_create(&path, dentry); 19562306a36Sopenharmony_ci return err; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci/** 19962306a36Sopenharmony_ci * ksmbd_vfs_mkdir() - vfs helper for smb create directory 20062306a36Sopenharmony_ci * @work: work 20162306a36Sopenharmony_ci * @name: directory name that is relative to share 20262306a36Sopenharmony_ci * @mode: directory create mode 20362306a36Sopenharmony_ci * 20462306a36Sopenharmony_ci * Return: 0 on success, otherwise error 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_ciint ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci struct mnt_idmap *idmap; 20962306a36Sopenharmony_ci struct path path; 21062306a36Sopenharmony_ci struct dentry *dentry; 21162306a36Sopenharmony_ci int err; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci dentry = ksmbd_vfs_kern_path_create(work, name, 21462306a36Sopenharmony_ci LOOKUP_NO_SYMLINKS | LOOKUP_DIRECTORY, 21562306a36Sopenharmony_ci &path); 21662306a36Sopenharmony_ci if (IS_ERR(dentry)) { 21762306a36Sopenharmony_ci err = PTR_ERR(dentry); 21862306a36Sopenharmony_ci if (err != -EEXIST) 21962306a36Sopenharmony_ci ksmbd_debug(VFS, "path create failed for %s, err %d\n", 22062306a36Sopenharmony_ci name, err); 22162306a36Sopenharmony_ci return err; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci idmap = mnt_idmap(path.mnt); 22562306a36Sopenharmony_ci mode |= S_IFDIR; 22662306a36Sopenharmony_ci err = vfs_mkdir(idmap, d_inode(path.dentry), dentry, mode); 22762306a36Sopenharmony_ci if (!err && d_unhashed(dentry)) { 22862306a36Sopenharmony_ci struct dentry *d; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci d = lookup_one(idmap, dentry->d_name.name, dentry->d_parent, 23162306a36Sopenharmony_ci dentry->d_name.len); 23262306a36Sopenharmony_ci if (IS_ERR(d)) { 23362306a36Sopenharmony_ci err = PTR_ERR(d); 23462306a36Sopenharmony_ci goto out_err; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci if (unlikely(d_is_negative(d))) { 23762306a36Sopenharmony_ci dput(d); 23862306a36Sopenharmony_ci err = -ENOENT; 23962306a36Sopenharmony_ci goto out_err; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), d_inode(d)); 24362306a36Sopenharmony_ci dput(d); 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ciout_err: 24762306a36Sopenharmony_ci done_path_create(&path, dentry); 24862306a36Sopenharmony_ci if (err) 24962306a36Sopenharmony_ci pr_err("mkdir(%s): creation failed (err:%d)\n", name, err); 25062306a36Sopenharmony_ci return err; 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic ssize_t ksmbd_vfs_getcasexattr(struct mnt_idmap *idmap, 25462306a36Sopenharmony_ci struct dentry *dentry, char *attr_name, 25562306a36Sopenharmony_ci int attr_name_len, char **attr_value) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci char *name, *xattr_list = NULL; 25862306a36Sopenharmony_ci ssize_t value_len = -ENOENT, xattr_list_len; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); 26162306a36Sopenharmony_ci if (xattr_list_len <= 0) 26262306a36Sopenharmony_ci goto out; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci for (name = xattr_list; name - xattr_list < xattr_list_len; 26562306a36Sopenharmony_ci name += strlen(name) + 1) { 26662306a36Sopenharmony_ci ksmbd_debug(VFS, "%s, len %zd\n", name, strlen(name)); 26762306a36Sopenharmony_ci if (strncasecmp(attr_name, name, attr_name_len)) 26862306a36Sopenharmony_ci continue; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci value_len = ksmbd_vfs_getxattr(idmap, 27162306a36Sopenharmony_ci dentry, 27262306a36Sopenharmony_ci name, 27362306a36Sopenharmony_ci attr_value); 27462306a36Sopenharmony_ci if (value_len < 0) 27562306a36Sopenharmony_ci pr_err("failed to get xattr in file\n"); 27662306a36Sopenharmony_ci break; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ciout: 28062306a36Sopenharmony_ci kvfree(xattr_list); 28162306a36Sopenharmony_ci return value_len; 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic int ksmbd_vfs_stream_read(struct ksmbd_file *fp, char *buf, loff_t *pos, 28562306a36Sopenharmony_ci size_t count) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci ssize_t v_len; 28862306a36Sopenharmony_ci char *stream_buf = NULL; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci ksmbd_debug(VFS, "read stream data pos : %llu, count : %zd\n", 29162306a36Sopenharmony_ci *pos, count); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci v_len = ksmbd_vfs_getcasexattr(file_mnt_idmap(fp->filp), 29462306a36Sopenharmony_ci fp->filp->f_path.dentry, 29562306a36Sopenharmony_ci fp->stream.name, 29662306a36Sopenharmony_ci fp->stream.size, 29762306a36Sopenharmony_ci &stream_buf); 29862306a36Sopenharmony_ci if ((int)v_len <= 0) 29962306a36Sopenharmony_ci return (int)v_len; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci if (v_len <= *pos) { 30262306a36Sopenharmony_ci count = -EINVAL; 30362306a36Sopenharmony_ci goto free_buf; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (v_len - *pos < count) 30762306a36Sopenharmony_ci count = v_len - *pos; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci memcpy(buf, &stream_buf[*pos], count); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cifree_buf: 31262306a36Sopenharmony_ci kvfree(stream_buf); 31362306a36Sopenharmony_ci return count; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci/** 31762306a36Sopenharmony_ci * check_lock_range() - vfs helper for smb byte range file locking 31862306a36Sopenharmony_ci * @filp: the file to apply the lock to 31962306a36Sopenharmony_ci * @start: lock start byte offset 32062306a36Sopenharmony_ci * @end: lock end byte offset 32162306a36Sopenharmony_ci * @type: byte range type read/write 32262306a36Sopenharmony_ci * 32362306a36Sopenharmony_ci * Return: 0 on success, otherwise error 32462306a36Sopenharmony_ci */ 32562306a36Sopenharmony_cistatic int check_lock_range(struct file *filp, loff_t start, loff_t end, 32662306a36Sopenharmony_ci unsigned char type) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci struct file_lock *flock; 32962306a36Sopenharmony_ci struct file_lock_context *ctx = locks_inode_context(file_inode(filp)); 33062306a36Sopenharmony_ci int error = 0; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci if (!ctx || list_empty_careful(&ctx->flc_posix)) 33362306a36Sopenharmony_ci return 0; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci spin_lock(&ctx->flc_lock); 33662306a36Sopenharmony_ci list_for_each_entry(flock, &ctx->flc_posix, fl_list) { 33762306a36Sopenharmony_ci /* check conflict locks */ 33862306a36Sopenharmony_ci if (flock->fl_end >= start && end >= flock->fl_start) { 33962306a36Sopenharmony_ci if (flock->fl_type == F_RDLCK) { 34062306a36Sopenharmony_ci if (type == WRITE) { 34162306a36Sopenharmony_ci pr_err("not allow write by shared lock\n"); 34262306a36Sopenharmony_ci error = 1; 34362306a36Sopenharmony_ci goto out; 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci } else if (flock->fl_type == F_WRLCK) { 34662306a36Sopenharmony_ci /* check owner in lock */ 34762306a36Sopenharmony_ci if (flock->fl_file != filp) { 34862306a36Sopenharmony_ci error = 1; 34962306a36Sopenharmony_ci pr_err("not allow rw access by exclusive lock from other opens\n"); 35062306a36Sopenharmony_ci goto out; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ciout: 35662306a36Sopenharmony_ci spin_unlock(&ctx->flc_lock); 35762306a36Sopenharmony_ci return error; 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci/** 36162306a36Sopenharmony_ci * ksmbd_vfs_read() - vfs helper for smb file read 36262306a36Sopenharmony_ci * @work: smb work 36362306a36Sopenharmony_ci * @fid: file id of open file 36462306a36Sopenharmony_ci * @count: read byte count 36562306a36Sopenharmony_ci * @pos: file pos 36662306a36Sopenharmony_ci * @rbuf: read data buffer 36762306a36Sopenharmony_ci * 36862306a36Sopenharmony_ci * Return: number of read bytes on success, otherwise error 36962306a36Sopenharmony_ci */ 37062306a36Sopenharmony_ciint ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count, 37162306a36Sopenharmony_ci loff_t *pos, char *rbuf) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci struct file *filp = fp->filp; 37462306a36Sopenharmony_ci ssize_t nbytes = 0; 37562306a36Sopenharmony_ci struct inode *inode = file_inode(filp); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (S_ISDIR(inode->i_mode)) 37862306a36Sopenharmony_ci return -EISDIR; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (unlikely(count == 0)) 38162306a36Sopenharmony_ci return 0; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (work->conn->connection_type) { 38462306a36Sopenharmony_ci if (!(fp->daccess & (FILE_READ_DATA_LE | FILE_EXECUTE_LE))) { 38562306a36Sopenharmony_ci pr_err("no right to read(%pD)\n", fp->filp); 38662306a36Sopenharmony_ci return -EACCES; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (ksmbd_stream_fd(fp)) 39162306a36Sopenharmony_ci return ksmbd_vfs_stream_read(fp, rbuf, pos, count); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (!work->tcon->posix_extensions) { 39462306a36Sopenharmony_ci int ret; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci ret = check_lock_range(filp, *pos, *pos + count - 1, READ); 39762306a36Sopenharmony_ci if (ret) { 39862306a36Sopenharmony_ci pr_err("unable to read due to lock\n"); 39962306a36Sopenharmony_ci return -EAGAIN; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci nbytes = kernel_read(filp, rbuf, count, pos); 40462306a36Sopenharmony_ci if (nbytes < 0) { 40562306a36Sopenharmony_ci pr_err("smb read failed, err = %zd\n", nbytes); 40662306a36Sopenharmony_ci return nbytes; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci filp->f_pos = *pos; 41062306a36Sopenharmony_ci return nbytes; 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_cistatic int ksmbd_vfs_stream_write(struct ksmbd_file *fp, char *buf, loff_t *pos, 41462306a36Sopenharmony_ci size_t count) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci char *stream_buf = NULL, *wbuf; 41762306a36Sopenharmony_ci struct mnt_idmap *idmap = file_mnt_idmap(fp->filp); 41862306a36Sopenharmony_ci size_t size; 41962306a36Sopenharmony_ci ssize_t v_len; 42062306a36Sopenharmony_ci int err = 0; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci ksmbd_debug(VFS, "write stream data pos : %llu, count : %zd\n", 42362306a36Sopenharmony_ci *pos, count); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci size = *pos + count; 42662306a36Sopenharmony_ci if (size > XATTR_SIZE_MAX) { 42762306a36Sopenharmony_ci size = XATTR_SIZE_MAX; 42862306a36Sopenharmony_ci count = (*pos + count) - XATTR_SIZE_MAX; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci v_len = ksmbd_vfs_getcasexattr(idmap, 43262306a36Sopenharmony_ci fp->filp->f_path.dentry, 43362306a36Sopenharmony_ci fp->stream.name, 43462306a36Sopenharmony_ci fp->stream.size, 43562306a36Sopenharmony_ci &stream_buf); 43662306a36Sopenharmony_ci if (v_len < 0) { 43762306a36Sopenharmony_ci pr_err("not found stream in xattr : %zd\n", v_len); 43862306a36Sopenharmony_ci err = v_len; 43962306a36Sopenharmony_ci goto out; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (v_len < size) { 44362306a36Sopenharmony_ci wbuf = kvzalloc(size, GFP_KERNEL); 44462306a36Sopenharmony_ci if (!wbuf) { 44562306a36Sopenharmony_ci err = -ENOMEM; 44662306a36Sopenharmony_ci goto out; 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci if (v_len > 0) 45062306a36Sopenharmony_ci memcpy(wbuf, stream_buf, v_len); 45162306a36Sopenharmony_ci kvfree(stream_buf); 45262306a36Sopenharmony_ci stream_buf = wbuf; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci memcpy(&stream_buf[*pos], buf, count); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci err = ksmbd_vfs_setxattr(idmap, 45862306a36Sopenharmony_ci &fp->filp->f_path, 45962306a36Sopenharmony_ci fp->stream.name, 46062306a36Sopenharmony_ci (void *)stream_buf, 46162306a36Sopenharmony_ci size, 46262306a36Sopenharmony_ci 0, 46362306a36Sopenharmony_ci true); 46462306a36Sopenharmony_ci if (err < 0) 46562306a36Sopenharmony_ci goto out; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci fp->filp->f_pos = *pos; 46862306a36Sopenharmony_ci err = 0; 46962306a36Sopenharmony_ciout: 47062306a36Sopenharmony_ci kvfree(stream_buf); 47162306a36Sopenharmony_ci return err; 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci/** 47562306a36Sopenharmony_ci * ksmbd_vfs_write() - vfs helper for smb file write 47662306a36Sopenharmony_ci * @work: work 47762306a36Sopenharmony_ci * @fid: file id of open file 47862306a36Sopenharmony_ci * @buf: buf containing data for writing 47962306a36Sopenharmony_ci * @count: read byte count 48062306a36Sopenharmony_ci * @pos: file pos 48162306a36Sopenharmony_ci * @sync: fsync after write 48262306a36Sopenharmony_ci * @written: number of bytes written 48362306a36Sopenharmony_ci * 48462306a36Sopenharmony_ci * Return: 0 on success, otherwise error 48562306a36Sopenharmony_ci */ 48662306a36Sopenharmony_ciint ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp, 48762306a36Sopenharmony_ci char *buf, size_t count, loff_t *pos, bool sync, 48862306a36Sopenharmony_ci ssize_t *written) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci struct file *filp; 49162306a36Sopenharmony_ci loff_t offset = *pos; 49262306a36Sopenharmony_ci int err = 0; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (work->conn->connection_type) { 49562306a36Sopenharmony_ci if (!(fp->daccess & FILE_WRITE_DATA_LE)) { 49662306a36Sopenharmony_ci pr_err("no right to write(%pD)\n", fp->filp); 49762306a36Sopenharmony_ci err = -EACCES; 49862306a36Sopenharmony_ci goto out; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci filp = fp->filp; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (ksmbd_stream_fd(fp)) { 50562306a36Sopenharmony_ci err = ksmbd_vfs_stream_write(fp, buf, pos, count); 50662306a36Sopenharmony_ci if (!err) 50762306a36Sopenharmony_ci *written = count; 50862306a36Sopenharmony_ci goto out; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci if (!work->tcon->posix_extensions) { 51262306a36Sopenharmony_ci err = check_lock_range(filp, *pos, *pos + count - 1, WRITE); 51362306a36Sopenharmony_ci if (err) { 51462306a36Sopenharmony_ci pr_err("unable to write due to lock\n"); 51562306a36Sopenharmony_ci err = -EAGAIN; 51662306a36Sopenharmony_ci goto out; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci /* Reserve lease break for parent dir at closing time */ 52162306a36Sopenharmony_ci fp->reserve_lease_break = true; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* Do we need to break any of a levelII oplock? */ 52462306a36Sopenharmony_ci smb_break_all_levII_oplock(work, fp, 1); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci err = kernel_write(filp, buf, count, pos); 52762306a36Sopenharmony_ci if (err < 0) { 52862306a36Sopenharmony_ci ksmbd_debug(VFS, "smb write failed, err = %d\n", err); 52962306a36Sopenharmony_ci goto out; 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci filp->f_pos = *pos; 53362306a36Sopenharmony_ci *written = err; 53462306a36Sopenharmony_ci err = 0; 53562306a36Sopenharmony_ci if (sync) { 53662306a36Sopenharmony_ci err = vfs_fsync_range(filp, offset, offset + *written, 0); 53762306a36Sopenharmony_ci if (err < 0) 53862306a36Sopenharmony_ci pr_err("fsync failed for filename = %pD, err = %d\n", 53962306a36Sopenharmony_ci fp->filp, err); 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ciout: 54362306a36Sopenharmony_ci return err; 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci/** 54762306a36Sopenharmony_ci * ksmbd_vfs_getattr() - vfs helper for smb getattr 54862306a36Sopenharmony_ci * @work: work 54962306a36Sopenharmony_ci * @fid: file id of open file 55062306a36Sopenharmony_ci * @attrs: inode attributes 55162306a36Sopenharmony_ci * 55262306a36Sopenharmony_ci * Return: 0 on success, otherwise error 55362306a36Sopenharmony_ci */ 55462306a36Sopenharmony_ciint ksmbd_vfs_getattr(const struct path *path, struct kstat *stat) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci int err; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci err = vfs_getattr(path, stat, STATX_BTIME, AT_STATX_SYNC_AS_STAT); 55962306a36Sopenharmony_ci if (err) 56062306a36Sopenharmony_ci pr_err("getattr failed, err %d\n", err); 56162306a36Sopenharmony_ci return err; 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci/** 56562306a36Sopenharmony_ci * ksmbd_vfs_fsync() - vfs helper for smb fsync 56662306a36Sopenharmony_ci * @work: work 56762306a36Sopenharmony_ci * @fid: file id of open file 56862306a36Sopenharmony_ci * 56962306a36Sopenharmony_ci * Return: 0 on success, otherwise error 57062306a36Sopenharmony_ci */ 57162306a36Sopenharmony_ciint ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci struct ksmbd_file *fp; 57462306a36Sopenharmony_ci int err; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci fp = ksmbd_lookup_fd_slow(work, fid, p_id); 57762306a36Sopenharmony_ci if (!fp) { 57862306a36Sopenharmony_ci pr_err("failed to get filp for fid %llu\n", fid); 57962306a36Sopenharmony_ci return -ENOENT; 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci err = vfs_fsync(fp->filp, 0); 58262306a36Sopenharmony_ci if (err < 0) 58362306a36Sopenharmony_ci pr_err("smb fsync failed, err = %d\n", err); 58462306a36Sopenharmony_ci ksmbd_fd_put(work, fp); 58562306a36Sopenharmony_ci return err; 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci/** 58962306a36Sopenharmony_ci * ksmbd_vfs_remove_file() - vfs helper for smb rmdir or unlink 59062306a36Sopenharmony_ci * @name: directory or file name that is relative to share 59162306a36Sopenharmony_ci * 59262306a36Sopenharmony_ci * Return: 0 on success, otherwise error 59362306a36Sopenharmony_ci */ 59462306a36Sopenharmony_ciint ksmbd_vfs_remove_file(struct ksmbd_work *work, const struct path *path) 59562306a36Sopenharmony_ci{ 59662306a36Sopenharmony_ci struct mnt_idmap *idmap; 59762306a36Sopenharmony_ci struct dentry *parent = path->dentry->d_parent; 59862306a36Sopenharmony_ci int err; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci if (ksmbd_override_fsids(work)) 60162306a36Sopenharmony_ci return -ENOMEM; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci if (!d_inode(path->dentry)->i_nlink) { 60462306a36Sopenharmony_ci err = -ENOENT; 60562306a36Sopenharmony_ci goto out_err; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci idmap = mnt_idmap(path->mnt); 60962306a36Sopenharmony_ci if (S_ISDIR(d_inode(path->dentry)->i_mode)) { 61062306a36Sopenharmony_ci err = vfs_rmdir(idmap, d_inode(parent), path->dentry); 61162306a36Sopenharmony_ci if (err && err != -ENOTEMPTY) 61262306a36Sopenharmony_ci ksmbd_debug(VFS, "rmdir failed, err %d\n", err); 61362306a36Sopenharmony_ci } else { 61462306a36Sopenharmony_ci err = vfs_unlink(idmap, d_inode(parent), path->dentry, NULL); 61562306a36Sopenharmony_ci if (err) 61662306a36Sopenharmony_ci ksmbd_debug(VFS, "unlink failed, err %d\n", err); 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ciout_err: 62062306a36Sopenharmony_ci ksmbd_revert_fsids(work); 62162306a36Sopenharmony_ci return err; 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci/** 62562306a36Sopenharmony_ci * ksmbd_vfs_link() - vfs helper for creating smb hardlink 62662306a36Sopenharmony_ci * @oldname: source file name 62762306a36Sopenharmony_ci * @newname: hardlink name that is relative to share 62862306a36Sopenharmony_ci * 62962306a36Sopenharmony_ci * Return: 0 on success, otherwise error 63062306a36Sopenharmony_ci */ 63162306a36Sopenharmony_ciint ksmbd_vfs_link(struct ksmbd_work *work, const char *oldname, 63262306a36Sopenharmony_ci const char *newname) 63362306a36Sopenharmony_ci{ 63462306a36Sopenharmony_ci struct path oldpath, newpath; 63562306a36Sopenharmony_ci struct dentry *dentry; 63662306a36Sopenharmony_ci int err; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci if (ksmbd_override_fsids(work)) 63962306a36Sopenharmony_ci return -ENOMEM; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci err = kern_path(oldname, LOOKUP_NO_SYMLINKS, &oldpath); 64262306a36Sopenharmony_ci if (err) { 64362306a36Sopenharmony_ci pr_err("cannot get linux path for %s, err = %d\n", 64462306a36Sopenharmony_ci oldname, err); 64562306a36Sopenharmony_ci goto out1; 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci dentry = ksmbd_vfs_kern_path_create(work, newname, 64962306a36Sopenharmony_ci LOOKUP_NO_SYMLINKS | LOOKUP_REVAL, 65062306a36Sopenharmony_ci &newpath); 65162306a36Sopenharmony_ci if (IS_ERR(dentry)) { 65262306a36Sopenharmony_ci err = PTR_ERR(dentry); 65362306a36Sopenharmony_ci pr_err("path create err for %s, err %d\n", newname, err); 65462306a36Sopenharmony_ci goto out2; 65562306a36Sopenharmony_ci } 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci err = -EXDEV; 65862306a36Sopenharmony_ci if (oldpath.mnt != newpath.mnt) { 65962306a36Sopenharmony_ci pr_err("vfs_link failed err %d\n", err); 66062306a36Sopenharmony_ci goto out3; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci err = vfs_link(oldpath.dentry, mnt_idmap(newpath.mnt), 66462306a36Sopenharmony_ci d_inode(newpath.dentry), 66562306a36Sopenharmony_ci dentry, NULL); 66662306a36Sopenharmony_ci if (err) 66762306a36Sopenharmony_ci ksmbd_debug(VFS, "vfs_link failed err %d\n", err); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ciout3: 67062306a36Sopenharmony_ci done_path_create(&newpath, dentry); 67162306a36Sopenharmony_ciout2: 67262306a36Sopenharmony_ci path_put(&oldpath); 67362306a36Sopenharmony_ciout1: 67462306a36Sopenharmony_ci ksmbd_revert_fsids(work); 67562306a36Sopenharmony_ci return err; 67662306a36Sopenharmony_ci} 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ciint ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path, 67962306a36Sopenharmony_ci char *newname, int flags) 68062306a36Sopenharmony_ci{ 68162306a36Sopenharmony_ci struct dentry *old_parent, *new_dentry, *trap; 68262306a36Sopenharmony_ci struct dentry *old_child = old_path->dentry; 68362306a36Sopenharmony_ci struct path new_path; 68462306a36Sopenharmony_ci struct qstr new_last; 68562306a36Sopenharmony_ci struct renamedata rd; 68662306a36Sopenharmony_ci struct filename *to; 68762306a36Sopenharmony_ci struct ksmbd_share_config *share_conf = work->tcon->share_conf; 68862306a36Sopenharmony_ci struct ksmbd_file *parent_fp; 68962306a36Sopenharmony_ci int new_type; 69062306a36Sopenharmony_ci int err, lookup_flags = LOOKUP_NO_SYMLINKS; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci if (ksmbd_override_fsids(work)) 69362306a36Sopenharmony_ci return -ENOMEM; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci to = getname_kernel(newname); 69662306a36Sopenharmony_ci if (IS_ERR(to)) { 69762306a36Sopenharmony_ci err = PTR_ERR(to); 69862306a36Sopenharmony_ci goto revert_fsids; 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ciretry: 70262306a36Sopenharmony_ci err = vfs_path_parent_lookup(to, lookup_flags | LOOKUP_BENEATH, 70362306a36Sopenharmony_ci &new_path, &new_last, &new_type, 70462306a36Sopenharmony_ci &share_conf->vfs_path); 70562306a36Sopenharmony_ci if (err) 70662306a36Sopenharmony_ci goto out1; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci if (old_path->mnt != new_path.mnt) { 70962306a36Sopenharmony_ci err = -EXDEV; 71062306a36Sopenharmony_ci goto out2; 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci err = mnt_want_write(old_path->mnt); 71462306a36Sopenharmony_ci if (err) 71562306a36Sopenharmony_ci goto out2; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci trap = lock_rename_child(old_child, new_path.dentry); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci old_parent = dget(old_child->d_parent); 72062306a36Sopenharmony_ci if (d_unhashed(old_child)) { 72162306a36Sopenharmony_ci err = -EINVAL; 72262306a36Sopenharmony_ci goto out3; 72362306a36Sopenharmony_ci } 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci parent_fp = ksmbd_lookup_fd_inode(old_child->d_parent); 72662306a36Sopenharmony_ci if (parent_fp) { 72762306a36Sopenharmony_ci if (parent_fp->daccess & FILE_DELETE_LE) { 72862306a36Sopenharmony_ci pr_err("parent dir is opened with delete access\n"); 72962306a36Sopenharmony_ci err = -ESHARE; 73062306a36Sopenharmony_ci ksmbd_fd_put(work, parent_fp); 73162306a36Sopenharmony_ci goto out3; 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci ksmbd_fd_put(work, parent_fp); 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci new_dentry = lookup_one_qstr_excl(&new_last, new_path.dentry, 73762306a36Sopenharmony_ci lookup_flags | LOOKUP_RENAME_TARGET); 73862306a36Sopenharmony_ci if (IS_ERR(new_dentry)) { 73962306a36Sopenharmony_ci err = PTR_ERR(new_dentry); 74062306a36Sopenharmony_ci goto out3; 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (d_is_symlink(new_dentry)) { 74462306a36Sopenharmony_ci err = -EACCES; 74562306a36Sopenharmony_ci goto out4; 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci if ((flags & RENAME_NOREPLACE) && d_is_positive(new_dentry)) { 74962306a36Sopenharmony_ci err = -EEXIST; 75062306a36Sopenharmony_ci goto out4; 75162306a36Sopenharmony_ci } 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci if (old_child == trap) { 75462306a36Sopenharmony_ci err = -EINVAL; 75562306a36Sopenharmony_ci goto out4; 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci if (new_dentry == trap) { 75962306a36Sopenharmony_ci err = -ENOTEMPTY; 76062306a36Sopenharmony_ci goto out4; 76162306a36Sopenharmony_ci } 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci rd.old_mnt_idmap = mnt_idmap(old_path->mnt), 76462306a36Sopenharmony_ci rd.old_dir = d_inode(old_parent), 76562306a36Sopenharmony_ci rd.old_dentry = old_child, 76662306a36Sopenharmony_ci rd.new_mnt_idmap = mnt_idmap(new_path.mnt), 76762306a36Sopenharmony_ci rd.new_dir = new_path.dentry->d_inode, 76862306a36Sopenharmony_ci rd.new_dentry = new_dentry, 76962306a36Sopenharmony_ci rd.flags = flags, 77062306a36Sopenharmony_ci rd.delegated_inode = NULL, 77162306a36Sopenharmony_ci err = vfs_rename(&rd); 77262306a36Sopenharmony_ci if (err) 77362306a36Sopenharmony_ci ksmbd_debug(VFS, "vfs_rename failed err %d\n", err); 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ciout4: 77662306a36Sopenharmony_ci dput(new_dentry); 77762306a36Sopenharmony_ciout3: 77862306a36Sopenharmony_ci dput(old_parent); 77962306a36Sopenharmony_ci unlock_rename(old_parent, new_path.dentry); 78062306a36Sopenharmony_ci mnt_drop_write(old_path->mnt); 78162306a36Sopenharmony_ciout2: 78262306a36Sopenharmony_ci path_put(&new_path); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci if (retry_estale(err, lookup_flags)) { 78562306a36Sopenharmony_ci lookup_flags |= LOOKUP_REVAL; 78662306a36Sopenharmony_ci goto retry; 78762306a36Sopenharmony_ci } 78862306a36Sopenharmony_ciout1: 78962306a36Sopenharmony_ci putname(to); 79062306a36Sopenharmony_cirevert_fsids: 79162306a36Sopenharmony_ci ksmbd_revert_fsids(work); 79262306a36Sopenharmony_ci return err; 79362306a36Sopenharmony_ci} 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci/** 79662306a36Sopenharmony_ci * ksmbd_vfs_truncate() - vfs helper for smb file truncate 79762306a36Sopenharmony_ci * @work: work 79862306a36Sopenharmony_ci * @fid: file id of old file 79962306a36Sopenharmony_ci * @size: truncate to given size 80062306a36Sopenharmony_ci * 80162306a36Sopenharmony_ci * Return: 0 on success, otherwise error 80262306a36Sopenharmony_ci */ 80362306a36Sopenharmony_ciint ksmbd_vfs_truncate(struct ksmbd_work *work, 80462306a36Sopenharmony_ci struct ksmbd_file *fp, loff_t size) 80562306a36Sopenharmony_ci{ 80662306a36Sopenharmony_ci int err = 0; 80762306a36Sopenharmony_ci struct file *filp; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci filp = fp->filp; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci /* Do we need to break any of a levelII oplock? */ 81262306a36Sopenharmony_ci smb_break_all_levII_oplock(work, fp, 1); 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci if (!work->tcon->posix_extensions) { 81562306a36Sopenharmony_ci struct inode *inode = file_inode(filp); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci if (size < inode->i_size) { 81862306a36Sopenharmony_ci err = check_lock_range(filp, size, 81962306a36Sopenharmony_ci inode->i_size - 1, WRITE); 82062306a36Sopenharmony_ci } else { 82162306a36Sopenharmony_ci err = check_lock_range(filp, inode->i_size, 82262306a36Sopenharmony_ci size - 1, WRITE); 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci if (err) { 82662306a36Sopenharmony_ci pr_err("failed due to lock\n"); 82762306a36Sopenharmony_ci return -EAGAIN; 82862306a36Sopenharmony_ci } 82962306a36Sopenharmony_ci } 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci err = vfs_truncate(&filp->f_path, size); 83262306a36Sopenharmony_ci if (err) 83362306a36Sopenharmony_ci pr_err("truncate failed, err %d\n", err); 83462306a36Sopenharmony_ci return err; 83562306a36Sopenharmony_ci} 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci/** 83862306a36Sopenharmony_ci * ksmbd_vfs_listxattr() - vfs helper for smb list extended attributes 83962306a36Sopenharmony_ci * @dentry: dentry of file for listing xattrs 84062306a36Sopenharmony_ci * @list: destination buffer 84162306a36Sopenharmony_ci * @size: destination buffer length 84262306a36Sopenharmony_ci * 84362306a36Sopenharmony_ci * Return: xattr list length on success, otherwise error 84462306a36Sopenharmony_ci */ 84562306a36Sopenharmony_cissize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list) 84662306a36Sopenharmony_ci{ 84762306a36Sopenharmony_ci ssize_t size; 84862306a36Sopenharmony_ci char *vlist = NULL; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci size = vfs_listxattr(dentry, NULL, 0); 85162306a36Sopenharmony_ci if (size <= 0) 85262306a36Sopenharmony_ci return size; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci vlist = kvzalloc(size, GFP_KERNEL); 85562306a36Sopenharmony_ci if (!vlist) 85662306a36Sopenharmony_ci return -ENOMEM; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci *list = vlist; 85962306a36Sopenharmony_ci size = vfs_listxattr(dentry, vlist, size); 86062306a36Sopenharmony_ci if (size < 0) { 86162306a36Sopenharmony_ci ksmbd_debug(VFS, "listxattr failed\n"); 86262306a36Sopenharmony_ci kvfree(vlist); 86362306a36Sopenharmony_ci *list = NULL; 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci return size; 86762306a36Sopenharmony_ci} 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_cistatic ssize_t ksmbd_vfs_xattr_len(struct mnt_idmap *idmap, 87062306a36Sopenharmony_ci struct dentry *dentry, char *xattr_name) 87162306a36Sopenharmony_ci{ 87262306a36Sopenharmony_ci return vfs_getxattr(idmap, dentry, xattr_name, NULL, 0); 87362306a36Sopenharmony_ci} 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci/** 87662306a36Sopenharmony_ci * ksmbd_vfs_getxattr() - vfs helper for smb get extended attributes value 87762306a36Sopenharmony_ci * @idmap: idmap 87862306a36Sopenharmony_ci * @dentry: dentry of file for getting xattrs 87962306a36Sopenharmony_ci * @xattr_name: name of xattr name to query 88062306a36Sopenharmony_ci * @xattr_buf: destination buffer xattr value 88162306a36Sopenharmony_ci * 88262306a36Sopenharmony_ci * Return: read xattr value length on success, otherwise error 88362306a36Sopenharmony_ci */ 88462306a36Sopenharmony_cissize_t ksmbd_vfs_getxattr(struct mnt_idmap *idmap, 88562306a36Sopenharmony_ci struct dentry *dentry, 88662306a36Sopenharmony_ci char *xattr_name, char **xattr_buf) 88762306a36Sopenharmony_ci{ 88862306a36Sopenharmony_ci ssize_t xattr_len; 88962306a36Sopenharmony_ci char *buf; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci *xattr_buf = NULL; 89262306a36Sopenharmony_ci xattr_len = ksmbd_vfs_xattr_len(idmap, dentry, xattr_name); 89362306a36Sopenharmony_ci if (xattr_len < 0) 89462306a36Sopenharmony_ci return xattr_len; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci buf = kmalloc(xattr_len + 1, GFP_KERNEL); 89762306a36Sopenharmony_ci if (!buf) 89862306a36Sopenharmony_ci return -ENOMEM; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci xattr_len = vfs_getxattr(idmap, dentry, xattr_name, 90162306a36Sopenharmony_ci (void *)buf, xattr_len); 90262306a36Sopenharmony_ci if (xattr_len > 0) 90362306a36Sopenharmony_ci *xattr_buf = buf; 90462306a36Sopenharmony_ci else 90562306a36Sopenharmony_ci kfree(buf); 90662306a36Sopenharmony_ci return xattr_len; 90762306a36Sopenharmony_ci} 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci/** 91062306a36Sopenharmony_ci * ksmbd_vfs_setxattr() - vfs helper for smb set extended attributes value 91162306a36Sopenharmony_ci * @idmap: idmap of the relevant mount 91262306a36Sopenharmony_ci * @path: path of dentry to set XATTR at 91362306a36Sopenharmony_ci * @attr_name: xattr name for setxattr 91462306a36Sopenharmony_ci * @attr_value: xattr value to set 91562306a36Sopenharmony_ci * @attr_size: size of xattr value 91662306a36Sopenharmony_ci * @flags: destination buffer length 91762306a36Sopenharmony_ci * @get_write: get write access to a mount 91862306a36Sopenharmony_ci * 91962306a36Sopenharmony_ci * Return: 0 on success, otherwise error 92062306a36Sopenharmony_ci */ 92162306a36Sopenharmony_ciint ksmbd_vfs_setxattr(struct mnt_idmap *idmap, 92262306a36Sopenharmony_ci const struct path *path, const char *attr_name, 92362306a36Sopenharmony_ci void *attr_value, size_t attr_size, int flags, 92462306a36Sopenharmony_ci bool get_write) 92562306a36Sopenharmony_ci{ 92662306a36Sopenharmony_ci int err; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci if (get_write == true) { 92962306a36Sopenharmony_ci err = mnt_want_write(path->mnt); 93062306a36Sopenharmony_ci if (err) 93162306a36Sopenharmony_ci return err; 93262306a36Sopenharmony_ci } 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci err = vfs_setxattr(idmap, 93562306a36Sopenharmony_ci path->dentry, 93662306a36Sopenharmony_ci attr_name, 93762306a36Sopenharmony_ci attr_value, 93862306a36Sopenharmony_ci attr_size, 93962306a36Sopenharmony_ci flags); 94062306a36Sopenharmony_ci if (err) 94162306a36Sopenharmony_ci ksmbd_debug(VFS, "setxattr failed, err %d\n", err); 94262306a36Sopenharmony_ci if (get_write == true) 94362306a36Sopenharmony_ci mnt_drop_write(path->mnt); 94462306a36Sopenharmony_ci return err; 94562306a36Sopenharmony_ci} 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci/** 94862306a36Sopenharmony_ci * ksmbd_vfs_set_fadvise() - convert smb IO caching options to linux options 94962306a36Sopenharmony_ci * @filp: file pointer for IO 95062306a36Sopenharmony_ci * @options: smb IO options 95162306a36Sopenharmony_ci */ 95262306a36Sopenharmony_civoid ksmbd_vfs_set_fadvise(struct file *filp, __le32 option) 95362306a36Sopenharmony_ci{ 95462306a36Sopenharmony_ci struct address_space *mapping; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci mapping = filp->f_mapping; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci if (!option || !mapping) 95962306a36Sopenharmony_ci return; 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci if (option & FILE_WRITE_THROUGH_LE) { 96262306a36Sopenharmony_ci filp->f_flags |= O_SYNC; 96362306a36Sopenharmony_ci } else if (option & FILE_SEQUENTIAL_ONLY_LE) { 96462306a36Sopenharmony_ci filp->f_ra.ra_pages = inode_to_bdi(mapping->host)->ra_pages * 2; 96562306a36Sopenharmony_ci spin_lock(&filp->f_lock); 96662306a36Sopenharmony_ci filp->f_mode &= ~FMODE_RANDOM; 96762306a36Sopenharmony_ci spin_unlock(&filp->f_lock); 96862306a36Sopenharmony_ci } else if (option & FILE_RANDOM_ACCESS_LE) { 96962306a36Sopenharmony_ci spin_lock(&filp->f_lock); 97062306a36Sopenharmony_ci filp->f_mode |= FMODE_RANDOM; 97162306a36Sopenharmony_ci spin_unlock(&filp->f_lock); 97262306a36Sopenharmony_ci } 97362306a36Sopenharmony_ci} 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ciint ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp, 97662306a36Sopenharmony_ci loff_t off, loff_t len) 97762306a36Sopenharmony_ci{ 97862306a36Sopenharmony_ci smb_break_all_levII_oplock(work, fp, 1); 97962306a36Sopenharmony_ci if (fp->f_ci->m_fattr & FILE_ATTRIBUTE_SPARSE_FILE_LE) 98062306a36Sopenharmony_ci return vfs_fallocate(fp->filp, 98162306a36Sopenharmony_ci FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 98262306a36Sopenharmony_ci off, len); 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci return vfs_fallocate(fp->filp, 98562306a36Sopenharmony_ci FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE, 98662306a36Sopenharmony_ci off, len); 98762306a36Sopenharmony_ci} 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ciint ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length, 99062306a36Sopenharmony_ci struct file_allocated_range_buffer *ranges, 99162306a36Sopenharmony_ci unsigned int in_count, unsigned int *out_count) 99262306a36Sopenharmony_ci{ 99362306a36Sopenharmony_ci struct file *f = fp->filp; 99462306a36Sopenharmony_ci struct inode *inode = file_inode(fp->filp); 99562306a36Sopenharmony_ci loff_t maxbytes = (u64)inode->i_sb->s_maxbytes, end; 99662306a36Sopenharmony_ci loff_t extent_start, extent_end; 99762306a36Sopenharmony_ci int ret = 0; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci if (start > maxbytes) 100062306a36Sopenharmony_ci return -EFBIG; 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci if (!in_count) 100362306a36Sopenharmony_ci return 0; 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci /* 100662306a36Sopenharmony_ci * Shrink request scope to what the fs can actually handle. 100762306a36Sopenharmony_ci */ 100862306a36Sopenharmony_ci if (length > maxbytes || (maxbytes - length) < start) 100962306a36Sopenharmony_ci length = maxbytes - start; 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci if (start + length > inode->i_size) 101262306a36Sopenharmony_ci length = inode->i_size - start; 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci *out_count = 0; 101562306a36Sopenharmony_ci end = start + length; 101662306a36Sopenharmony_ci while (start < end && *out_count < in_count) { 101762306a36Sopenharmony_ci extent_start = vfs_llseek(f, start, SEEK_DATA); 101862306a36Sopenharmony_ci if (extent_start < 0) { 101962306a36Sopenharmony_ci if (extent_start != -ENXIO) 102062306a36Sopenharmony_ci ret = (int)extent_start; 102162306a36Sopenharmony_ci break; 102262306a36Sopenharmony_ci } 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci if (extent_start >= end) 102562306a36Sopenharmony_ci break; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci extent_end = vfs_llseek(f, extent_start, SEEK_HOLE); 102862306a36Sopenharmony_ci if (extent_end < 0) { 102962306a36Sopenharmony_ci if (extent_end != -ENXIO) 103062306a36Sopenharmony_ci ret = (int)extent_end; 103162306a36Sopenharmony_ci break; 103262306a36Sopenharmony_ci } else if (extent_start >= extent_end) { 103362306a36Sopenharmony_ci break; 103462306a36Sopenharmony_ci } 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci ranges[*out_count].file_offset = cpu_to_le64(extent_start); 103762306a36Sopenharmony_ci ranges[(*out_count)++].length = 103862306a36Sopenharmony_ci cpu_to_le64(min(extent_end, end) - extent_start); 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci start = extent_end; 104162306a36Sopenharmony_ci } 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci return ret; 104462306a36Sopenharmony_ci} 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ciint ksmbd_vfs_remove_xattr(struct mnt_idmap *idmap, 104762306a36Sopenharmony_ci const struct path *path, char *attr_name) 104862306a36Sopenharmony_ci{ 104962306a36Sopenharmony_ci int err; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci err = mnt_want_write(path->mnt); 105262306a36Sopenharmony_ci if (err) 105362306a36Sopenharmony_ci return err; 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci err = vfs_removexattr(idmap, path->dentry, attr_name); 105662306a36Sopenharmony_ci mnt_drop_write(path->mnt); 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci return err; 105962306a36Sopenharmony_ci} 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ciint ksmbd_vfs_unlink(struct file *filp) 106262306a36Sopenharmony_ci{ 106362306a36Sopenharmony_ci int err = 0; 106462306a36Sopenharmony_ci struct dentry *dir, *dentry = filp->f_path.dentry; 106562306a36Sopenharmony_ci struct mnt_idmap *idmap = file_mnt_idmap(filp); 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci err = mnt_want_write(filp->f_path.mnt); 106862306a36Sopenharmony_ci if (err) 106962306a36Sopenharmony_ci return err; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci dir = dget_parent(dentry); 107262306a36Sopenharmony_ci err = ksmbd_vfs_lock_parent(dir, dentry); 107362306a36Sopenharmony_ci if (err) 107462306a36Sopenharmony_ci goto out; 107562306a36Sopenharmony_ci dget(dentry); 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci if (S_ISDIR(d_inode(dentry)->i_mode)) 107862306a36Sopenharmony_ci err = vfs_rmdir(idmap, d_inode(dir), dentry); 107962306a36Sopenharmony_ci else 108062306a36Sopenharmony_ci err = vfs_unlink(idmap, d_inode(dir), dentry, NULL); 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci dput(dentry); 108362306a36Sopenharmony_ci inode_unlock(d_inode(dir)); 108462306a36Sopenharmony_ci if (err) 108562306a36Sopenharmony_ci ksmbd_debug(VFS, "failed to delete, err %d\n", err); 108662306a36Sopenharmony_ciout: 108762306a36Sopenharmony_ci dput(dir); 108862306a36Sopenharmony_ci mnt_drop_write(filp->f_path.mnt); 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci return err; 109162306a36Sopenharmony_ci} 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_cistatic bool __dir_empty(struct dir_context *ctx, const char *name, int namlen, 109462306a36Sopenharmony_ci loff_t offset, u64 ino, unsigned int d_type) 109562306a36Sopenharmony_ci{ 109662306a36Sopenharmony_ci struct ksmbd_readdir_data *buf; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci buf = container_of(ctx, struct ksmbd_readdir_data, ctx); 109962306a36Sopenharmony_ci buf->dirent_count++; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci return buf->dirent_count <= 2; 110262306a36Sopenharmony_ci} 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci/** 110562306a36Sopenharmony_ci * ksmbd_vfs_empty_dir() - check for empty directory 110662306a36Sopenharmony_ci * @fp: ksmbd file pointer 110762306a36Sopenharmony_ci * 110862306a36Sopenharmony_ci * Return: true if directory empty, otherwise false 110962306a36Sopenharmony_ci */ 111062306a36Sopenharmony_ciint ksmbd_vfs_empty_dir(struct ksmbd_file *fp) 111162306a36Sopenharmony_ci{ 111262306a36Sopenharmony_ci int err; 111362306a36Sopenharmony_ci struct ksmbd_readdir_data readdir_data; 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci memset(&readdir_data, 0, sizeof(struct ksmbd_readdir_data)); 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci set_ctx_actor(&readdir_data.ctx, __dir_empty); 111862306a36Sopenharmony_ci readdir_data.dirent_count = 0; 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci err = iterate_dir(fp->filp, &readdir_data.ctx); 112162306a36Sopenharmony_ci if (readdir_data.dirent_count > 2) 112262306a36Sopenharmony_ci err = -ENOTEMPTY; 112362306a36Sopenharmony_ci else 112462306a36Sopenharmony_ci err = 0; 112562306a36Sopenharmony_ci return err; 112662306a36Sopenharmony_ci} 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_cistatic bool __caseless_lookup(struct dir_context *ctx, const char *name, 112962306a36Sopenharmony_ci int namlen, loff_t offset, u64 ino, 113062306a36Sopenharmony_ci unsigned int d_type) 113162306a36Sopenharmony_ci{ 113262306a36Sopenharmony_ci struct ksmbd_readdir_data *buf; 113362306a36Sopenharmony_ci int cmp = -EINVAL; 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci buf = container_of(ctx, struct ksmbd_readdir_data, ctx); 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci if (buf->used != namlen) 113862306a36Sopenharmony_ci return true; 113962306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_UNICODE) && buf->um) { 114062306a36Sopenharmony_ci const struct qstr q_buf = {.name = buf->private, 114162306a36Sopenharmony_ci .len = buf->used}; 114262306a36Sopenharmony_ci const struct qstr q_name = {.name = name, 114362306a36Sopenharmony_ci .len = namlen}; 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci cmp = utf8_strncasecmp(buf->um, &q_buf, &q_name); 114662306a36Sopenharmony_ci } 114762306a36Sopenharmony_ci if (cmp < 0) 114862306a36Sopenharmony_ci cmp = strncasecmp((char *)buf->private, name, namlen); 114962306a36Sopenharmony_ci if (!cmp) { 115062306a36Sopenharmony_ci memcpy((char *)buf->private, name, namlen); 115162306a36Sopenharmony_ci buf->dirent_count = 1; 115262306a36Sopenharmony_ci return false; 115362306a36Sopenharmony_ci } 115462306a36Sopenharmony_ci return true; 115562306a36Sopenharmony_ci} 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci/** 115862306a36Sopenharmony_ci * ksmbd_vfs_lookup_in_dir() - lookup a file in a directory 115962306a36Sopenharmony_ci * @dir: path info 116062306a36Sopenharmony_ci * @name: filename to lookup 116162306a36Sopenharmony_ci * @namelen: filename length 116262306a36Sopenharmony_ci * 116362306a36Sopenharmony_ci * Return: 0 on success, otherwise error 116462306a36Sopenharmony_ci */ 116562306a36Sopenharmony_cistatic int ksmbd_vfs_lookup_in_dir(const struct path *dir, char *name, 116662306a36Sopenharmony_ci size_t namelen, struct unicode_map *um) 116762306a36Sopenharmony_ci{ 116862306a36Sopenharmony_ci int ret; 116962306a36Sopenharmony_ci struct file *dfilp; 117062306a36Sopenharmony_ci int flags = O_RDONLY | O_LARGEFILE; 117162306a36Sopenharmony_ci struct ksmbd_readdir_data readdir_data = { 117262306a36Sopenharmony_ci .ctx.actor = __caseless_lookup, 117362306a36Sopenharmony_ci .private = name, 117462306a36Sopenharmony_ci .used = namelen, 117562306a36Sopenharmony_ci .dirent_count = 0, 117662306a36Sopenharmony_ci .um = um, 117762306a36Sopenharmony_ci }; 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci dfilp = dentry_open(dir, flags, current_cred()); 118062306a36Sopenharmony_ci if (IS_ERR(dfilp)) 118162306a36Sopenharmony_ci return PTR_ERR(dfilp); 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci ret = iterate_dir(dfilp, &readdir_data.ctx); 118462306a36Sopenharmony_ci if (readdir_data.dirent_count > 0) 118562306a36Sopenharmony_ci ret = 0; 118662306a36Sopenharmony_ci fput(dfilp); 118762306a36Sopenharmony_ci return ret; 118862306a36Sopenharmony_ci} 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci/** 119162306a36Sopenharmony_ci * ksmbd_vfs_kern_path_locked() - lookup a file and get path info 119262306a36Sopenharmony_ci * @name: file path that is relative to share 119362306a36Sopenharmony_ci * @flags: lookup flags 119462306a36Sopenharmony_ci * @parent_path: if lookup succeed, return parent_path info 119562306a36Sopenharmony_ci * @path: if lookup succeed, return path info 119662306a36Sopenharmony_ci * @caseless: caseless filename lookup 119762306a36Sopenharmony_ci * 119862306a36Sopenharmony_ci * Return: 0 on success, otherwise error 119962306a36Sopenharmony_ci */ 120062306a36Sopenharmony_ciint ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name, 120162306a36Sopenharmony_ci unsigned int flags, struct path *parent_path, 120262306a36Sopenharmony_ci struct path *path, bool caseless) 120362306a36Sopenharmony_ci{ 120462306a36Sopenharmony_ci struct ksmbd_share_config *share_conf = work->tcon->share_conf; 120562306a36Sopenharmony_ci int err; 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci err = ksmbd_vfs_path_lookup_locked(share_conf, name, flags, parent_path, 120862306a36Sopenharmony_ci path); 120962306a36Sopenharmony_ci if (!err) 121062306a36Sopenharmony_ci return 0; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci if (caseless) { 121362306a36Sopenharmony_ci char *filepath; 121462306a36Sopenharmony_ci size_t path_len, remain_len; 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci filepath = kstrdup(name, GFP_KERNEL); 121762306a36Sopenharmony_ci if (!filepath) 121862306a36Sopenharmony_ci return -ENOMEM; 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci path_len = strlen(filepath); 122162306a36Sopenharmony_ci remain_len = path_len; 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci *parent_path = share_conf->vfs_path; 122462306a36Sopenharmony_ci path_get(parent_path); 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci while (d_can_lookup(parent_path->dentry)) { 122762306a36Sopenharmony_ci char *filename = filepath + path_len - remain_len; 122862306a36Sopenharmony_ci char *next = strchrnul(filename, '/'); 122962306a36Sopenharmony_ci size_t filename_len = next - filename; 123062306a36Sopenharmony_ci bool is_last = !next[0]; 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci if (filename_len == 0) 123362306a36Sopenharmony_ci break; 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci err = ksmbd_vfs_lookup_in_dir(parent_path, filename, 123662306a36Sopenharmony_ci filename_len, 123762306a36Sopenharmony_ci work->conn->um); 123862306a36Sopenharmony_ci if (err) 123962306a36Sopenharmony_ci goto out2; 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci next[0] = '\0'; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci err = vfs_path_lookup(share_conf->vfs_path.dentry, 124462306a36Sopenharmony_ci share_conf->vfs_path.mnt, 124562306a36Sopenharmony_ci filepath, 124662306a36Sopenharmony_ci flags, 124762306a36Sopenharmony_ci path); 124862306a36Sopenharmony_ci if (err) 124962306a36Sopenharmony_ci goto out2; 125062306a36Sopenharmony_ci else if (is_last) 125162306a36Sopenharmony_ci goto out1; 125262306a36Sopenharmony_ci path_put(parent_path); 125362306a36Sopenharmony_ci *parent_path = *path; 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci next[0] = '/'; 125662306a36Sopenharmony_ci remain_len -= filename_len + 1; 125762306a36Sopenharmony_ci } 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci err = -EINVAL; 126062306a36Sopenharmony_ciout2: 126162306a36Sopenharmony_ci path_put(parent_path); 126262306a36Sopenharmony_ciout1: 126362306a36Sopenharmony_ci kfree(filepath); 126462306a36Sopenharmony_ci } 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci if (!err) { 126762306a36Sopenharmony_ci err = mnt_want_write(parent_path->mnt); 126862306a36Sopenharmony_ci if (err) { 126962306a36Sopenharmony_ci path_put(path); 127062306a36Sopenharmony_ci path_put(parent_path); 127162306a36Sopenharmony_ci return err; 127262306a36Sopenharmony_ci } 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci err = ksmbd_vfs_lock_parent(parent_path->dentry, path->dentry); 127562306a36Sopenharmony_ci if (err) { 127662306a36Sopenharmony_ci path_put(path); 127762306a36Sopenharmony_ci path_put(parent_path); 127862306a36Sopenharmony_ci } 127962306a36Sopenharmony_ci } 128062306a36Sopenharmony_ci return err; 128162306a36Sopenharmony_ci} 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_civoid ksmbd_vfs_kern_path_unlock(struct path *parent_path, struct path *path) 128462306a36Sopenharmony_ci{ 128562306a36Sopenharmony_ci inode_unlock(d_inode(parent_path->dentry)); 128662306a36Sopenharmony_ci mnt_drop_write(parent_path->mnt); 128762306a36Sopenharmony_ci path_put(path); 128862306a36Sopenharmony_ci path_put(parent_path); 128962306a36Sopenharmony_ci} 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_cistruct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work, 129262306a36Sopenharmony_ci const char *name, 129362306a36Sopenharmony_ci unsigned int flags, 129462306a36Sopenharmony_ci struct path *path) 129562306a36Sopenharmony_ci{ 129662306a36Sopenharmony_ci char *abs_name; 129762306a36Sopenharmony_ci struct dentry *dent; 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci abs_name = convert_to_unix_name(work->tcon->share_conf, name); 130062306a36Sopenharmony_ci if (!abs_name) 130162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci dent = kern_path_create(AT_FDCWD, abs_name, path, flags); 130462306a36Sopenharmony_ci kfree(abs_name); 130562306a36Sopenharmony_ci return dent; 130662306a36Sopenharmony_ci} 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ciint ksmbd_vfs_remove_acl_xattrs(struct mnt_idmap *idmap, 130962306a36Sopenharmony_ci const struct path *path) 131062306a36Sopenharmony_ci{ 131162306a36Sopenharmony_ci char *name, *xattr_list = NULL; 131262306a36Sopenharmony_ci ssize_t xattr_list_len; 131362306a36Sopenharmony_ci int err = 0; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list); 131662306a36Sopenharmony_ci if (xattr_list_len < 0) { 131762306a36Sopenharmony_ci goto out; 131862306a36Sopenharmony_ci } else if (!xattr_list_len) { 131962306a36Sopenharmony_ci ksmbd_debug(SMB, "empty xattr in the file\n"); 132062306a36Sopenharmony_ci goto out; 132162306a36Sopenharmony_ci } 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci err = mnt_want_write(path->mnt); 132462306a36Sopenharmony_ci if (err) 132562306a36Sopenharmony_ci goto out; 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci for (name = xattr_list; name - xattr_list < xattr_list_len; 132862306a36Sopenharmony_ci name += strlen(name) + 1) { 132962306a36Sopenharmony_ci ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name)); 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci if (!strncmp(name, XATTR_NAME_POSIX_ACL_ACCESS, 133262306a36Sopenharmony_ci sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1) || 133362306a36Sopenharmony_ci !strncmp(name, XATTR_NAME_POSIX_ACL_DEFAULT, 133462306a36Sopenharmony_ci sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1)) { 133562306a36Sopenharmony_ci err = vfs_remove_acl(idmap, path->dentry, name); 133662306a36Sopenharmony_ci if (err) 133762306a36Sopenharmony_ci ksmbd_debug(SMB, 133862306a36Sopenharmony_ci "remove acl xattr failed : %s\n", name); 133962306a36Sopenharmony_ci } 134062306a36Sopenharmony_ci } 134162306a36Sopenharmony_ci mnt_drop_write(path->mnt); 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ciout: 134462306a36Sopenharmony_ci kvfree(xattr_list); 134562306a36Sopenharmony_ci return err; 134662306a36Sopenharmony_ci} 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ciint ksmbd_vfs_remove_sd_xattrs(struct mnt_idmap *idmap, const struct path *path) 134962306a36Sopenharmony_ci{ 135062306a36Sopenharmony_ci char *name, *xattr_list = NULL; 135162306a36Sopenharmony_ci ssize_t xattr_list_len; 135262306a36Sopenharmony_ci int err = 0; 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci xattr_list_len = ksmbd_vfs_listxattr(path->dentry, &xattr_list); 135562306a36Sopenharmony_ci if (xattr_list_len < 0) { 135662306a36Sopenharmony_ci goto out; 135762306a36Sopenharmony_ci } else if (!xattr_list_len) { 135862306a36Sopenharmony_ci ksmbd_debug(SMB, "empty xattr in the file\n"); 135962306a36Sopenharmony_ci goto out; 136062306a36Sopenharmony_ci } 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci for (name = xattr_list; name - xattr_list < xattr_list_len; 136362306a36Sopenharmony_ci name += strlen(name) + 1) { 136462306a36Sopenharmony_ci ksmbd_debug(SMB, "%s, len %zd\n", name, strlen(name)); 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci if (!strncmp(name, XATTR_NAME_SD, XATTR_NAME_SD_LEN)) { 136762306a36Sopenharmony_ci err = ksmbd_vfs_remove_xattr(idmap, path, name); 136862306a36Sopenharmony_ci if (err) 136962306a36Sopenharmony_ci ksmbd_debug(SMB, "remove xattr failed : %s\n", name); 137062306a36Sopenharmony_ci } 137162306a36Sopenharmony_ci } 137262306a36Sopenharmony_ciout: 137362306a36Sopenharmony_ci kvfree(xattr_list); 137462306a36Sopenharmony_ci return err; 137562306a36Sopenharmony_ci} 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_cistatic struct xattr_smb_acl *ksmbd_vfs_make_xattr_posix_acl(struct mnt_idmap *idmap, 137862306a36Sopenharmony_ci struct inode *inode, 137962306a36Sopenharmony_ci int acl_type) 138062306a36Sopenharmony_ci{ 138162306a36Sopenharmony_ci struct xattr_smb_acl *smb_acl = NULL; 138262306a36Sopenharmony_ci struct posix_acl *posix_acls; 138362306a36Sopenharmony_ci struct posix_acl_entry *pa_entry; 138462306a36Sopenharmony_ci struct xattr_acl_entry *xa_entry; 138562306a36Sopenharmony_ci int i; 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_FS_POSIX_ACL)) 138862306a36Sopenharmony_ci return NULL; 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci posix_acls = get_inode_acl(inode, acl_type); 139162306a36Sopenharmony_ci if (IS_ERR_OR_NULL(posix_acls)) 139262306a36Sopenharmony_ci return NULL; 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci smb_acl = kzalloc(sizeof(struct xattr_smb_acl) + 139562306a36Sopenharmony_ci sizeof(struct xattr_acl_entry) * posix_acls->a_count, 139662306a36Sopenharmony_ci GFP_KERNEL); 139762306a36Sopenharmony_ci if (!smb_acl) 139862306a36Sopenharmony_ci goto out; 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci smb_acl->count = posix_acls->a_count; 140162306a36Sopenharmony_ci pa_entry = posix_acls->a_entries; 140262306a36Sopenharmony_ci xa_entry = smb_acl->entries; 140362306a36Sopenharmony_ci for (i = 0; i < posix_acls->a_count; i++, pa_entry++, xa_entry++) { 140462306a36Sopenharmony_ci switch (pa_entry->e_tag) { 140562306a36Sopenharmony_ci case ACL_USER: 140662306a36Sopenharmony_ci xa_entry->type = SMB_ACL_USER; 140762306a36Sopenharmony_ci xa_entry->uid = posix_acl_uid_translate(idmap, pa_entry); 140862306a36Sopenharmony_ci break; 140962306a36Sopenharmony_ci case ACL_USER_OBJ: 141062306a36Sopenharmony_ci xa_entry->type = SMB_ACL_USER_OBJ; 141162306a36Sopenharmony_ci break; 141262306a36Sopenharmony_ci case ACL_GROUP: 141362306a36Sopenharmony_ci xa_entry->type = SMB_ACL_GROUP; 141462306a36Sopenharmony_ci xa_entry->gid = posix_acl_gid_translate(idmap, pa_entry); 141562306a36Sopenharmony_ci break; 141662306a36Sopenharmony_ci case ACL_GROUP_OBJ: 141762306a36Sopenharmony_ci xa_entry->type = SMB_ACL_GROUP_OBJ; 141862306a36Sopenharmony_ci break; 141962306a36Sopenharmony_ci case ACL_OTHER: 142062306a36Sopenharmony_ci xa_entry->type = SMB_ACL_OTHER; 142162306a36Sopenharmony_ci break; 142262306a36Sopenharmony_ci case ACL_MASK: 142362306a36Sopenharmony_ci xa_entry->type = SMB_ACL_MASK; 142462306a36Sopenharmony_ci break; 142562306a36Sopenharmony_ci default: 142662306a36Sopenharmony_ci pr_err("unknown type : 0x%x\n", pa_entry->e_tag); 142762306a36Sopenharmony_ci goto out; 142862306a36Sopenharmony_ci } 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci if (pa_entry->e_perm & ACL_READ) 143162306a36Sopenharmony_ci xa_entry->perm |= SMB_ACL_READ; 143262306a36Sopenharmony_ci if (pa_entry->e_perm & ACL_WRITE) 143362306a36Sopenharmony_ci xa_entry->perm |= SMB_ACL_WRITE; 143462306a36Sopenharmony_ci if (pa_entry->e_perm & ACL_EXECUTE) 143562306a36Sopenharmony_ci xa_entry->perm |= SMB_ACL_EXECUTE; 143662306a36Sopenharmony_ci } 143762306a36Sopenharmony_ciout: 143862306a36Sopenharmony_ci posix_acl_release(posix_acls); 143962306a36Sopenharmony_ci return smb_acl; 144062306a36Sopenharmony_ci} 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ciint ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn, 144362306a36Sopenharmony_ci struct mnt_idmap *idmap, 144462306a36Sopenharmony_ci const struct path *path, 144562306a36Sopenharmony_ci struct smb_ntsd *pntsd, int len, 144662306a36Sopenharmony_ci bool get_write) 144762306a36Sopenharmony_ci{ 144862306a36Sopenharmony_ci int rc; 144962306a36Sopenharmony_ci struct ndr sd_ndr = {0}, acl_ndr = {0}; 145062306a36Sopenharmony_ci struct xattr_ntacl acl = {0}; 145162306a36Sopenharmony_ci struct xattr_smb_acl *smb_acl, *def_smb_acl = NULL; 145262306a36Sopenharmony_ci struct dentry *dentry = path->dentry; 145362306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci acl.version = 4; 145662306a36Sopenharmony_ci acl.hash_type = XATTR_SD_HASH_TYPE_SHA256; 145762306a36Sopenharmony_ci acl.current_time = ksmbd_UnixTimeToNT(current_time(inode)); 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci memcpy(acl.desc, "posix_acl", 9); 146062306a36Sopenharmony_ci acl.desc_len = 10; 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci pntsd->osidoffset = 146362306a36Sopenharmony_ci cpu_to_le32(le32_to_cpu(pntsd->osidoffset) + NDR_NTSD_OFFSETOF); 146462306a36Sopenharmony_ci pntsd->gsidoffset = 146562306a36Sopenharmony_ci cpu_to_le32(le32_to_cpu(pntsd->gsidoffset) + NDR_NTSD_OFFSETOF); 146662306a36Sopenharmony_ci pntsd->dacloffset = 146762306a36Sopenharmony_ci cpu_to_le32(le32_to_cpu(pntsd->dacloffset) + NDR_NTSD_OFFSETOF); 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci acl.sd_buf = (char *)pntsd; 147062306a36Sopenharmony_ci acl.sd_size = len; 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci rc = ksmbd_gen_sd_hash(conn, acl.sd_buf, acl.sd_size, acl.hash); 147362306a36Sopenharmony_ci if (rc) { 147462306a36Sopenharmony_ci pr_err("failed to generate hash for ndr acl\n"); 147562306a36Sopenharmony_ci return rc; 147662306a36Sopenharmony_ci } 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci smb_acl = ksmbd_vfs_make_xattr_posix_acl(idmap, inode, 147962306a36Sopenharmony_ci ACL_TYPE_ACCESS); 148062306a36Sopenharmony_ci if (S_ISDIR(inode->i_mode)) 148162306a36Sopenharmony_ci def_smb_acl = ksmbd_vfs_make_xattr_posix_acl(idmap, inode, 148262306a36Sopenharmony_ci ACL_TYPE_DEFAULT); 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci rc = ndr_encode_posix_acl(&acl_ndr, idmap, inode, 148562306a36Sopenharmony_ci smb_acl, def_smb_acl); 148662306a36Sopenharmony_ci if (rc) { 148762306a36Sopenharmony_ci pr_err("failed to encode ndr to posix acl\n"); 148862306a36Sopenharmony_ci goto out; 148962306a36Sopenharmony_ci } 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci rc = ksmbd_gen_sd_hash(conn, acl_ndr.data, acl_ndr.offset, 149262306a36Sopenharmony_ci acl.posix_acl_hash); 149362306a36Sopenharmony_ci if (rc) { 149462306a36Sopenharmony_ci pr_err("failed to generate hash for ndr acl\n"); 149562306a36Sopenharmony_ci goto out; 149662306a36Sopenharmony_ci } 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci rc = ndr_encode_v4_ntacl(&sd_ndr, &acl); 149962306a36Sopenharmony_ci if (rc) { 150062306a36Sopenharmony_ci pr_err("failed to encode ndr to posix acl\n"); 150162306a36Sopenharmony_ci goto out; 150262306a36Sopenharmony_ci } 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci rc = ksmbd_vfs_setxattr(idmap, path, 150562306a36Sopenharmony_ci XATTR_NAME_SD, sd_ndr.data, 150662306a36Sopenharmony_ci sd_ndr.offset, 0, get_write); 150762306a36Sopenharmony_ci if (rc < 0) 150862306a36Sopenharmony_ci pr_err("Failed to store XATTR ntacl :%d\n", rc); 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci kfree(sd_ndr.data); 151162306a36Sopenharmony_ciout: 151262306a36Sopenharmony_ci kfree(acl_ndr.data); 151362306a36Sopenharmony_ci kfree(smb_acl); 151462306a36Sopenharmony_ci kfree(def_smb_acl); 151562306a36Sopenharmony_ci return rc; 151662306a36Sopenharmony_ci} 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ciint ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn, 151962306a36Sopenharmony_ci struct mnt_idmap *idmap, 152062306a36Sopenharmony_ci struct dentry *dentry, 152162306a36Sopenharmony_ci struct smb_ntsd **pntsd) 152262306a36Sopenharmony_ci{ 152362306a36Sopenharmony_ci int rc; 152462306a36Sopenharmony_ci struct ndr n; 152562306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 152662306a36Sopenharmony_ci struct ndr acl_ndr = {0}; 152762306a36Sopenharmony_ci struct xattr_ntacl acl; 152862306a36Sopenharmony_ci struct xattr_smb_acl *smb_acl = NULL, *def_smb_acl = NULL; 152962306a36Sopenharmony_ci __u8 cmp_hash[XATTR_SD_HASH_SIZE] = {0}; 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_ci rc = ksmbd_vfs_getxattr(idmap, dentry, XATTR_NAME_SD, &n.data); 153262306a36Sopenharmony_ci if (rc <= 0) 153362306a36Sopenharmony_ci return rc; 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci n.length = rc; 153662306a36Sopenharmony_ci rc = ndr_decode_v4_ntacl(&n, &acl); 153762306a36Sopenharmony_ci if (rc) 153862306a36Sopenharmony_ci goto free_n_data; 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_ci smb_acl = ksmbd_vfs_make_xattr_posix_acl(idmap, inode, 154162306a36Sopenharmony_ci ACL_TYPE_ACCESS); 154262306a36Sopenharmony_ci if (S_ISDIR(inode->i_mode)) 154362306a36Sopenharmony_ci def_smb_acl = ksmbd_vfs_make_xattr_posix_acl(idmap, inode, 154462306a36Sopenharmony_ci ACL_TYPE_DEFAULT); 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci rc = ndr_encode_posix_acl(&acl_ndr, idmap, inode, smb_acl, 154762306a36Sopenharmony_ci def_smb_acl); 154862306a36Sopenharmony_ci if (rc) { 154962306a36Sopenharmony_ci pr_err("failed to encode ndr to posix acl\n"); 155062306a36Sopenharmony_ci goto out_free; 155162306a36Sopenharmony_ci } 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci rc = ksmbd_gen_sd_hash(conn, acl_ndr.data, acl_ndr.offset, cmp_hash); 155462306a36Sopenharmony_ci if (rc) { 155562306a36Sopenharmony_ci pr_err("failed to generate hash for ndr acl\n"); 155662306a36Sopenharmony_ci goto out_free; 155762306a36Sopenharmony_ci } 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci if (memcmp(cmp_hash, acl.posix_acl_hash, XATTR_SD_HASH_SIZE)) { 156062306a36Sopenharmony_ci pr_err("hash value diff\n"); 156162306a36Sopenharmony_ci rc = -EINVAL; 156262306a36Sopenharmony_ci goto out_free; 156362306a36Sopenharmony_ci } 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci *pntsd = acl.sd_buf; 156662306a36Sopenharmony_ci if (acl.sd_size < sizeof(struct smb_ntsd)) { 156762306a36Sopenharmony_ci pr_err("sd size is invalid\n"); 156862306a36Sopenharmony_ci goto out_free; 156962306a36Sopenharmony_ci } 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci (*pntsd)->osidoffset = cpu_to_le32(le32_to_cpu((*pntsd)->osidoffset) - 157262306a36Sopenharmony_ci NDR_NTSD_OFFSETOF); 157362306a36Sopenharmony_ci (*pntsd)->gsidoffset = cpu_to_le32(le32_to_cpu((*pntsd)->gsidoffset) - 157462306a36Sopenharmony_ci NDR_NTSD_OFFSETOF); 157562306a36Sopenharmony_ci (*pntsd)->dacloffset = cpu_to_le32(le32_to_cpu((*pntsd)->dacloffset) - 157662306a36Sopenharmony_ci NDR_NTSD_OFFSETOF); 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_ci rc = acl.sd_size; 157962306a36Sopenharmony_ciout_free: 158062306a36Sopenharmony_ci kfree(acl_ndr.data); 158162306a36Sopenharmony_ci kfree(smb_acl); 158262306a36Sopenharmony_ci kfree(def_smb_acl); 158362306a36Sopenharmony_ci if (rc < 0) { 158462306a36Sopenharmony_ci kfree(acl.sd_buf); 158562306a36Sopenharmony_ci *pntsd = NULL; 158662306a36Sopenharmony_ci } 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_cifree_n_data: 158962306a36Sopenharmony_ci kfree(n.data); 159062306a36Sopenharmony_ci return rc; 159162306a36Sopenharmony_ci} 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ciint ksmbd_vfs_set_dos_attrib_xattr(struct mnt_idmap *idmap, 159462306a36Sopenharmony_ci const struct path *path, 159562306a36Sopenharmony_ci struct xattr_dos_attrib *da, 159662306a36Sopenharmony_ci bool get_write) 159762306a36Sopenharmony_ci{ 159862306a36Sopenharmony_ci struct ndr n; 159962306a36Sopenharmony_ci int err; 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_ci err = ndr_encode_dos_attr(&n, da); 160262306a36Sopenharmony_ci if (err) 160362306a36Sopenharmony_ci return err; 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_ci err = ksmbd_vfs_setxattr(idmap, path, XATTR_NAME_DOS_ATTRIBUTE, 160662306a36Sopenharmony_ci (void *)n.data, n.offset, 0, get_write); 160762306a36Sopenharmony_ci if (err) 160862306a36Sopenharmony_ci ksmbd_debug(SMB, "failed to store dos attribute in xattr\n"); 160962306a36Sopenharmony_ci kfree(n.data); 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci return err; 161262306a36Sopenharmony_ci} 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_ciint ksmbd_vfs_get_dos_attrib_xattr(struct mnt_idmap *idmap, 161562306a36Sopenharmony_ci struct dentry *dentry, 161662306a36Sopenharmony_ci struct xattr_dos_attrib *da) 161762306a36Sopenharmony_ci{ 161862306a36Sopenharmony_ci struct ndr n; 161962306a36Sopenharmony_ci int err; 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci err = ksmbd_vfs_getxattr(idmap, dentry, XATTR_NAME_DOS_ATTRIBUTE, 162262306a36Sopenharmony_ci (char **)&n.data); 162362306a36Sopenharmony_ci if (err > 0) { 162462306a36Sopenharmony_ci n.length = err; 162562306a36Sopenharmony_ci if (ndr_decode_dos_attr(&n, da)) 162662306a36Sopenharmony_ci err = -EINVAL; 162762306a36Sopenharmony_ci kfree(n.data); 162862306a36Sopenharmony_ci } else { 162962306a36Sopenharmony_ci ksmbd_debug(SMB, "failed to load dos attribute in xattr\n"); 163062306a36Sopenharmony_ci } 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci return err; 163362306a36Sopenharmony_ci} 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ci/** 163662306a36Sopenharmony_ci * ksmbd_vfs_init_kstat() - convert unix stat information to smb stat format 163762306a36Sopenharmony_ci * @p: destination buffer 163862306a36Sopenharmony_ci * @ksmbd_kstat: ksmbd kstat wrapper 163962306a36Sopenharmony_ci */ 164062306a36Sopenharmony_civoid *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat) 164162306a36Sopenharmony_ci{ 164262306a36Sopenharmony_ci struct file_directory_info *info = (struct file_directory_info *)(*p); 164362306a36Sopenharmony_ci struct kstat *kstat = ksmbd_kstat->kstat; 164462306a36Sopenharmony_ci u64 time; 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_ci info->FileIndex = 0; 164762306a36Sopenharmony_ci info->CreationTime = cpu_to_le64(ksmbd_kstat->create_time); 164862306a36Sopenharmony_ci time = ksmbd_UnixTimeToNT(kstat->atime); 164962306a36Sopenharmony_ci info->LastAccessTime = cpu_to_le64(time); 165062306a36Sopenharmony_ci time = ksmbd_UnixTimeToNT(kstat->mtime); 165162306a36Sopenharmony_ci info->LastWriteTime = cpu_to_le64(time); 165262306a36Sopenharmony_ci time = ksmbd_UnixTimeToNT(kstat->ctime); 165362306a36Sopenharmony_ci info->ChangeTime = cpu_to_le64(time); 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci if (ksmbd_kstat->file_attributes & FILE_ATTRIBUTE_DIRECTORY_LE) { 165662306a36Sopenharmony_ci info->EndOfFile = 0; 165762306a36Sopenharmony_ci info->AllocationSize = 0; 165862306a36Sopenharmony_ci } else { 165962306a36Sopenharmony_ci info->EndOfFile = cpu_to_le64(kstat->size); 166062306a36Sopenharmony_ci info->AllocationSize = cpu_to_le64(kstat->blocks << 9); 166162306a36Sopenharmony_ci } 166262306a36Sopenharmony_ci info->ExtFileAttributes = ksmbd_kstat->file_attributes; 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci return info; 166562306a36Sopenharmony_ci} 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ciint ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work, 166862306a36Sopenharmony_ci struct mnt_idmap *idmap, 166962306a36Sopenharmony_ci struct dentry *dentry, 167062306a36Sopenharmony_ci struct ksmbd_kstat *ksmbd_kstat) 167162306a36Sopenharmony_ci{ 167262306a36Sopenharmony_ci u64 time; 167362306a36Sopenharmony_ci int rc; 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci generic_fillattr(idmap, STATX_BASIC_STATS, d_inode(dentry), 167662306a36Sopenharmony_ci ksmbd_kstat->kstat); 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci time = ksmbd_UnixTimeToNT(ksmbd_kstat->kstat->ctime); 167962306a36Sopenharmony_ci ksmbd_kstat->create_time = time; 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ci /* 168262306a36Sopenharmony_ci * set default value for the case that store dos attributes is not yes 168362306a36Sopenharmony_ci * or that acl is disable in server's filesystem and the config is yes. 168462306a36Sopenharmony_ci */ 168562306a36Sopenharmony_ci if (S_ISDIR(ksmbd_kstat->kstat->mode)) 168662306a36Sopenharmony_ci ksmbd_kstat->file_attributes = FILE_ATTRIBUTE_DIRECTORY_LE; 168762306a36Sopenharmony_ci else 168862306a36Sopenharmony_ci ksmbd_kstat->file_attributes = FILE_ATTRIBUTE_ARCHIVE_LE; 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_ci if (test_share_config_flag(work->tcon->share_conf, 169162306a36Sopenharmony_ci KSMBD_SHARE_FLAG_STORE_DOS_ATTRS)) { 169262306a36Sopenharmony_ci struct xattr_dos_attrib da; 169362306a36Sopenharmony_ci 169462306a36Sopenharmony_ci rc = ksmbd_vfs_get_dos_attrib_xattr(idmap, dentry, &da); 169562306a36Sopenharmony_ci if (rc > 0) { 169662306a36Sopenharmony_ci ksmbd_kstat->file_attributes = cpu_to_le32(da.attr); 169762306a36Sopenharmony_ci ksmbd_kstat->create_time = da.create_time; 169862306a36Sopenharmony_ci } else { 169962306a36Sopenharmony_ci ksmbd_debug(VFS, "fail to load dos attribute.\n"); 170062306a36Sopenharmony_ci } 170162306a36Sopenharmony_ci } 170262306a36Sopenharmony_ci 170362306a36Sopenharmony_ci return 0; 170462306a36Sopenharmony_ci} 170562306a36Sopenharmony_ci 170662306a36Sopenharmony_cissize_t ksmbd_vfs_casexattr_len(struct mnt_idmap *idmap, 170762306a36Sopenharmony_ci struct dentry *dentry, char *attr_name, 170862306a36Sopenharmony_ci int attr_name_len) 170962306a36Sopenharmony_ci{ 171062306a36Sopenharmony_ci char *name, *xattr_list = NULL; 171162306a36Sopenharmony_ci ssize_t value_len = -ENOENT, xattr_list_len; 171262306a36Sopenharmony_ci 171362306a36Sopenharmony_ci xattr_list_len = ksmbd_vfs_listxattr(dentry, &xattr_list); 171462306a36Sopenharmony_ci if (xattr_list_len <= 0) 171562306a36Sopenharmony_ci goto out; 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci for (name = xattr_list; name - xattr_list < xattr_list_len; 171862306a36Sopenharmony_ci name += strlen(name) + 1) { 171962306a36Sopenharmony_ci ksmbd_debug(VFS, "%s, len %zd\n", name, strlen(name)); 172062306a36Sopenharmony_ci if (strncasecmp(attr_name, name, attr_name_len)) 172162306a36Sopenharmony_ci continue; 172262306a36Sopenharmony_ci 172362306a36Sopenharmony_ci value_len = ksmbd_vfs_xattr_len(idmap, dentry, name); 172462306a36Sopenharmony_ci break; 172562306a36Sopenharmony_ci } 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_ciout: 172862306a36Sopenharmony_ci kvfree(xattr_list); 172962306a36Sopenharmony_ci return value_len; 173062306a36Sopenharmony_ci} 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_ciint ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name, 173362306a36Sopenharmony_ci size_t *xattr_stream_name_size, int s_type) 173462306a36Sopenharmony_ci{ 173562306a36Sopenharmony_ci char *type, *buf; 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_ci if (s_type == DIR_STREAM) 173862306a36Sopenharmony_ci type = ":$INDEX_ALLOCATION"; 173962306a36Sopenharmony_ci else 174062306a36Sopenharmony_ci type = ":$DATA"; 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_ci buf = kasprintf(GFP_KERNEL, "%s%s%s", 174362306a36Sopenharmony_ci XATTR_NAME_STREAM, stream_name, type); 174462306a36Sopenharmony_ci if (!buf) 174562306a36Sopenharmony_ci return -ENOMEM; 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci *xattr_stream_name = buf; 174862306a36Sopenharmony_ci *xattr_stream_name_size = strlen(buf) + 1; 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_ci return 0; 175162306a36Sopenharmony_ci} 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ciint ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work, 175462306a36Sopenharmony_ci struct ksmbd_file *src_fp, 175562306a36Sopenharmony_ci struct ksmbd_file *dst_fp, 175662306a36Sopenharmony_ci struct srv_copychunk *chunks, 175762306a36Sopenharmony_ci unsigned int chunk_count, 175862306a36Sopenharmony_ci unsigned int *chunk_count_written, 175962306a36Sopenharmony_ci unsigned int *chunk_size_written, 176062306a36Sopenharmony_ci loff_t *total_size_written) 176162306a36Sopenharmony_ci{ 176262306a36Sopenharmony_ci unsigned int i; 176362306a36Sopenharmony_ci loff_t src_off, dst_off, src_file_size; 176462306a36Sopenharmony_ci size_t len; 176562306a36Sopenharmony_ci int ret; 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci *chunk_count_written = 0; 176862306a36Sopenharmony_ci *chunk_size_written = 0; 176962306a36Sopenharmony_ci *total_size_written = 0; 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci if (!(src_fp->daccess & (FILE_READ_DATA_LE | FILE_EXECUTE_LE))) { 177262306a36Sopenharmony_ci pr_err("no right to read(%pD)\n", src_fp->filp); 177362306a36Sopenharmony_ci return -EACCES; 177462306a36Sopenharmony_ci } 177562306a36Sopenharmony_ci if (!(dst_fp->daccess & (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE))) { 177662306a36Sopenharmony_ci pr_err("no right to write(%pD)\n", dst_fp->filp); 177762306a36Sopenharmony_ci return -EACCES; 177862306a36Sopenharmony_ci } 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_ci if (ksmbd_stream_fd(src_fp) || ksmbd_stream_fd(dst_fp)) 178162306a36Sopenharmony_ci return -EBADF; 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_ci smb_break_all_levII_oplock(work, dst_fp, 1); 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci if (!work->tcon->posix_extensions) { 178662306a36Sopenharmony_ci for (i = 0; i < chunk_count; i++) { 178762306a36Sopenharmony_ci src_off = le64_to_cpu(chunks[i].SourceOffset); 178862306a36Sopenharmony_ci dst_off = le64_to_cpu(chunks[i].TargetOffset); 178962306a36Sopenharmony_ci len = le32_to_cpu(chunks[i].Length); 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_ci if (check_lock_range(src_fp->filp, src_off, 179262306a36Sopenharmony_ci src_off + len - 1, READ)) 179362306a36Sopenharmony_ci return -EAGAIN; 179462306a36Sopenharmony_ci if (check_lock_range(dst_fp->filp, dst_off, 179562306a36Sopenharmony_ci dst_off + len - 1, WRITE)) 179662306a36Sopenharmony_ci return -EAGAIN; 179762306a36Sopenharmony_ci } 179862306a36Sopenharmony_ci } 179962306a36Sopenharmony_ci 180062306a36Sopenharmony_ci src_file_size = i_size_read(file_inode(src_fp->filp)); 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci for (i = 0; i < chunk_count; i++) { 180362306a36Sopenharmony_ci src_off = le64_to_cpu(chunks[i].SourceOffset); 180462306a36Sopenharmony_ci dst_off = le64_to_cpu(chunks[i].TargetOffset); 180562306a36Sopenharmony_ci len = le32_to_cpu(chunks[i].Length); 180662306a36Sopenharmony_ci 180762306a36Sopenharmony_ci if (src_off + len > src_file_size) 180862306a36Sopenharmony_ci return -E2BIG; 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci ret = vfs_copy_file_range(src_fp->filp, src_off, 181162306a36Sopenharmony_ci dst_fp->filp, dst_off, len, 0); 181262306a36Sopenharmony_ci if (ret == -EOPNOTSUPP || ret == -EXDEV) 181362306a36Sopenharmony_ci ret = vfs_copy_file_range(src_fp->filp, src_off, 181462306a36Sopenharmony_ci dst_fp->filp, dst_off, len, 181562306a36Sopenharmony_ci COPY_FILE_SPLICE); 181662306a36Sopenharmony_ci if (ret < 0) 181762306a36Sopenharmony_ci return ret; 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_ci *chunk_count_written += 1; 182062306a36Sopenharmony_ci *total_size_written += ret; 182162306a36Sopenharmony_ci } 182262306a36Sopenharmony_ci return 0; 182362306a36Sopenharmony_ci} 182462306a36Sopenharmony_ci 182562306a36Sopenharmony_civoid ksmbd_vfs_posix_lock_wait(struct file_lock *flock) 182662306a36Sopenharmony_ci{ 182762306a36Sopenharmony_ci wait_event(flock->fl_wait, !flock->fl_blocker); 182862306a36Sopenharmony_ci} 182962306a36Sopenharmony_ci 183062306a36Sopenharmony_ciint ksmbd_vfs_posix_lock_wait_timeout(struct file_lock *flock, long timeout) 183162306a36Sopenharmony_ci{ 183262306a36Sopenharmony_ci return wait_event_interruptible_timeout(flock->fl_wait, 183362306a36Sopenharmony_ci !flock->fl_blocker, 183462306a36Sopenharmony_ci timeout); 183562306a36Sopenharmony_ci} 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_civoid ksmbd_vfs_posix_lock_unblock(struct file_lock *flock) 183862306a36Sopenharmony_ci{ 183962306a36Sopenharmony_ci locks_delete_block(flock); 184062306a36Sopenharmony_ci} 184162306a36Sopenharmony_ci 184262306a36Sopenharmony_ciint ksmbd_vfs_set_init_posix_acl(struct mnt_idmap *idmap, 184362306a36Sopenharmony_ci struct path *path) 184462306a36Sopenharmony_ci{ 184562306a36Sopenharmony_ci struct posix_acl_state acl_state; 184662306a36Sopenharmony_ci struct posix_acl *acls; 184762306a36Sopenharmony_ci struct dentry *dentry = path->dentry; 184862306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 184962306a36Sopenharmony_ci int rc; 185062306a36Sopenharmony_ci 185162306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_FS_POSIX_ACL)) 185262306a36Sopenharmony_ci return -EOPNOTSUPP; 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_ci ksmbd_debug(SMB, "Set posix acls\n"); 185562306a36Sopenharmony_ci rc = init_acl_state(&acl_state, 1); 185662306a36Sopenharmony_ci if (rc) 185762306a36Sopenharmony_ci return rc; 185862306a36Sopenharmony_ci 185962306a36Sopenharmony_ci /* Set default owner group */ 186062306a36Sopenharmony_ci acl_state.owner.allow = (inode->i_mode & 0700) >> 6; 186162306a36Sopenharmony_ci acl_state.group.allow = (inode->i_mode & 0070) >> 3; 186262306a36Sopenharmony_ci acl_state.other.allow = inode->i_mode & 0007; 186362306a36Sopenharmony_ci acl_state.users->aces[acl_state.users->n].uid = inode->i_uid; 186462306a36Sopenharmony_ci acl_state.users->aces[acl_state.users->n++].perms.allow = 186562306a36Sopenharmony_ci acl_state.owner.allow; 186662306a36Sopenharmony_ci acl_state.groups->aces[acl_state.groups->n].gid = inode->i_gid; 186762306a36Sopenharmony_ci acl_state.groups->aces[acl_state.groups->n++].perms.allow = 186862306a36Sopenharmony_ci acl_state.group.allow; 186962306a36Sopenharmony_ci acl_state.mask.allow = 0x07; 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_ci acls = posix_acl_alloc(6, GFP_KERNEL); 187262306a36Sopenharmony_ci if (!acls) { 187362306a36Sopenharmony_ci free_acl_state(&acl_state); 187462306a36Sopenharmony_ci return -ENOMEM; 187562306a36Sopenharmony_ci } 187662306a36Sopenharmony_ci posix_state_to_acl(&acl_state, acls->a_entries); 187762306a36Sopenharmony_ci 187862306a36Sopenharmony_ci rc = set_posix_acl(idmap, dentry, ACL_TYPE_ACCESS, acls); 187962306a36Sopenharmony_ci if (rc < 0) 188062306a36Sopenharmony_ci ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", 188162306a36Sopenharmony_ci rc); 188262306a36Sopenharmony_ci else if (S_ISDIR(inode->i_mode)) { 188362306a36Sopenharmony_ci posix_state_to_acl(&acl_state, acls->a_entries); 188462306a36Sopenharmony_ci rc = set_posix_acl(idmap, dentry, ACL_TYPE_DEFAULT, acls); 188562306a36Sopenharmony_ci if (rc < 0) 188662306a36Sopenharmony_ci ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", 188762306a36Sopenharmony_ci rc); 188862306a36Sopenharmony_ci } 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ci free_acl_state(&acl_state); 189162306a36Sopenharmony_ci posix_acl_release(acls); 189262306a36Sopenharmony_ci return rc; 189362306a36Sopenharmony_ci} 189462306a36Sopenharmony_ci 189562306a36Sopenharmony_ciint ksmbd_vfs_inherit_posix_acl(struct mnt_idmap *idmap, 189662306a36Sopenharmony_ci struct path *path, struct inode *parent_inode) 189762306a36Sopenharmony_ci{ 189862306a36Sopenharmony_ci struct posix_acl *acls; 189962306a36Sopenharmony_ci struct posix_acl_entry *pace; 190062306a36Sopenharmony_ci struct dentry *dentry = path->dentry; 190162306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 190262306a36Sopenharmony_ci int rc, i; 190362306a36Sopenharmony_ci 190462306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_FS_POSIX_ACL)) 190562306a36Sopenharmony_ci return -EOPNOTSUPP; 190662306a36Sopenharmony_ci 190762306a36Sopenharmony_ci acls = get_inode_acl(parent_inode, ACL_TYPE_DEFAULT); 190862306a36Sopenharmony_ci if (IS_ERR_OR_NULL(acls)) 190962306a36Sopenharmony_ci return -ENOENT; 191062306a36Sopenharmony_ci pace = acls->a_entries; 191162306a36Sopenharmony_ci 191262306a36Sopenharmony_ci for (i = 0; i < acls->a_count; i++, pace++) { 191362306a36Sopenharmony_ci if (pace->e_tag == ACL_MASK) { 191462306a36Sopenharmony_ci pace->e_perm = 0x07; 191562306a36Sopenharmony_ci break; 191662306a36Sopenharmony_ci } 191762306a36Sopenharmony_ci } 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_ci rc = set_posix_acl(idmap, dentry, ACL_TYPE_ACCESS, acls); 192062306a36Sopenharmony_ci if (rc < 0) 192162306a36Sopenharmony_ci ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_ACCESS) failed, rc : %d\n", 192262306a36Sopenharmony_ci rc); 192362306a36Sopenharmony_ci if (S_ISDIR(inode->i_mode)) { 192462306a36Sopenharmony_ci rc = set_posix_acl(idmap, dentry, ACL_TYPE_DEFAULT, 192562306a36Sopenharmony_ci acls); 192662306a36Sopenharmony_ci if (rc < 0) 192762306a36Sopenharmony_ci ksmbd_debug(SMB, "Set posix acl(ACL_TYPE_DEFAULT) failed, rc : %d\n", 192862306a36Sopenharmony_ci rc); 192962306a36Sopenharmony_ci } 193062306a36Sopenharmony_ci 193162306a36Sopenharmony_ci posix_acl_release(acls); 193262306a36Sopenharmony_ci return rc; 193362306a36Sopenharmony_ci} 1934