162306a36Sopenharmony_ci// SPDX-License-Identifier: LGPL-2.1 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) International Business Machines Corp., 2002,2010 562306a36Sopenharmony_ci * Author(s): Steve French (sfrench@us.ibm.com) 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/fs.h> 962306a36Sopenharmony_ci#include <linux/stat.h> 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci#include <linux/pagemap.h> 1262306a36Sopenharmony_ci#include <linux/freezer.h> 1362306a36Sopenharmony_ci#include <linux/sched/signal.h> 1462306a36Sopenharmony_ci#include <linux/wait_bit.h> 1562306a36Sopenharmony_ci#include <linux/fiemap.h> 1662306a36Sopenharmony_ci#include <asm/div64.h> 1762306a36Sopenharmony_ci#include "cifsfs.h" 1862306a36Sopenharmony_ci#include "cifspdu.h" 1962306a36Sopenharmony_ci#include "cifsglob.h" 2062306a36Sopenharmony_ci#include "cifsproto.h" 2162306a36Sopenharmony_ci#include "smb2proto.h" 2262306a36Sopenharmony_ci#include "cifs_debug.h" 2362306a36Sopenharmony_ci#include "cifs_fs_sb.h" 2462306a36Sopenharmony_ci#include "cifs_unicode.h" 2562306a36Sopenharmony_ci#include "fscache.h" 2662306a36Sopenharmony_ci#include "fs_context.h" 2762306a36Sopenharmony_ci#include "cifs_ioctl.h" 2862306a36Sopenharmony_ci#include "cached_dir.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic void cifs_set_ops(struct inode *inode) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci switch (inode->i_mode & S_IFMT) { 3562306a36Sopenharmony_ci case S_IFREG: 3662306a36Sopenharmony_ci inode->i_op = &cifs_file_inode_ops; 3762306a36Sopenharmony_ci if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) { 3862306a36Sopenharmony_ci if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) 3962306a36Sopenharmony_ci inode->i_fop = &cifs_file_direct_nobrl_ops; 4062306a36Sopenharmony_ci else 4162306a36Sopenharmony_ci inode->i_fop = &cifs_file_direct_ops; 4262306a36Sopenharmony_ci } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) { 4362306a36Sopenharmony_ci if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) 4462306a36Sopenharmony_ci inode->i_fop = &cifs_file_strict_nobrl_ops; 4562306a36Sopenharmony_ci else 4662306a36Sopenharmony_ci inode->i_fop = &cifs_file_strict_ops; 4762306a36Sopenharmony_ci } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) 4862306a36Sopenharmony_ci inode->i_fop = &cifs_file_nobrl_ops; 4962306a36Sopenharmony_ci else { /* not direct, send byte range locks */ 5062306a36Sopenharmony_ci inode->i_fop = &cifs_file_ops; 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci /* check if server can support readahead */ 5462306a36Sopenharmony_ci if (cifs_sb_master_tcon(cifs_sb)->ses->server->max_read < 5562306a36Sopenharmony_ci PAGE_SIZE + MAX_CIFS_HDR_SIZE) 5662306a36Sopenharmony_ci inode->i_data.a_ops = &cifs_addr_ops_smallbuf; 5762306a36Sopenharmony_ci else 5862306a36Sopenharmony_ci inode->i_data.a_ops = &cifs_addr_ops; 5962306a36Sopenharmony_ci break; 6062306a36Sopenharmony_ci case S_IFDIR: 6162306a36Sopenharmony_ci if (IS_AUTOMOUNT(inode)) { 6262306a36Sopenharmony_ci inode->i_op = &cifs_namespace_inode_operations; 6362306a36Sopenharmony_ci } else { 6462306a36Sopenharmony_ci inode->i_op = &cifs_dir_inode_ops; 6562306a36Sopenharmony_ci inode->i_fop = &cifs_dir_ops; 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci break; 6862306a36Sopenharmony_ci case S_IFLNK: 6962306a36Sopenharmony_ci inode->i_op = &cifs_symlink_inode_ops; 7062306a36Sopenharmony_ci break; 7162306a36Sopenharmony_ci default: 7262306a36Sopenharmony_ci init_special_inode(inode, inode->i_mode, inode->i_rdev); 7362306a36Sopenharmony_ci break; 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci/* check inode attributes against fattr. If they don't match, tag the 7862306a36Sopenharmony_ci * inode for cache invalidation 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_cistatic void 8162306a36Sopenharmony_cicifs_revalidate_cache(struct inode *inode, struct cifs_fattr *fattr) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci struct cifs_fscache_inode_coherency_data cd; 8462306a36Sopenharmony_ci struct cifsInodeInfo *cifs_i = CIFS_I(inode); 8562306a36Sopenharmony_ci struct timespec64 mtime; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci cifs_dbg(FYI, "%s: revalidating inode %llu\n", 8862306a36Sopenharmony_ci __func__, cifs_i->uniqueid); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci if (inode->i_state & I_NEW) { 9162306a36Sopenharmony_ci cifs_dbg(FYI, "%s: inode %llu is new\n", 9262306a36Sopenharmony_ci __func__, cifs_i->uniqueid); 9362306a36Sopenharmony_ci return; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci /* don't bother with revalidation if we have an oplock */ 9762306a36Sopenharmony_ci if (CIFS_CACHE_READ(cifs_i)) { 9862306a36Sopenharmony_ci cifs_dbg(FYI, "%s: inode %llu is oplocked\n", 9962306a36Sopenharmony_ci __func__, cifs_i->uniqueid); 10062306a36Sopenharmony_ci return; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* revalidate if mtime or size have changed */ 10462306a36Sopenharmony_ci fattr->cf_mtime = timestamp_truncate(fattr->cf_mtime, inode); 10562306a36Sopenharmony_ci mtime = inode_get_mtime(inode); 10662306a36Sopenharmony_ci if (timespec64_equal(&mtime, &fattr->cf_mtime) && 10762306a36Sopenharmony_ci cifs_i->server_eof == fattr->cf_eof) { 10862306a36Sopenharmony_ci cifs_dbg(FYI, "%s: inode %llu is unchanged\n", 10962306a36Sopenharmony_ci __func__, cifs_i->uniqueid); 11062306a36Sopenharmony_ci return; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci cifs_dbg(FYI, "%s: invalidating inode %llu mapping\n", 11462306a36Sopenharmony_ci __func__, cifs_i->uniqueid); 11562306a36Sopenharmony_ci set_bit(CIFS_INO_INVALID_MAPPING, &cifs_i->flags); 11662306a36Sopenharmony_ci /* Invalidate fscache cookie */ 11762306a36Sopenharmony_ci cifs_fscache_fill_coherency(&cifs_i->netfs.inode, &cd); 11862306a36Sopenharmony_ci fscache_invalidate(cifs_inode_cookie(inode), &cd, i_size_read(inode), 0); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/* 12262306a36Sopenharmony_ci * copy nlink to the inode, unless it wasn't provided. Provide 12362306a36Sopenharmony_ci * sane values if we don't have an existing one and none was provided 12462306a36Sopenharmony_ci */ 12562306a36Sopenharmony_cistatic void 12662306a36Sopenharmony_cicifs_nlink_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci /* 12962306a36Sopenharmony_ci * if we're in a situation where we can't trust what we 13062306a36Sopenharmony_ci * got from the server (readdir, some non-unix cases) 13162306a36Sopenharmony_ci * fake reasonable values 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_ci if (fattr->cf_flags & CIFS_FATTR_UNKNOWN_NLINK) { 13462306a36Sopenharmony_ci /* only provide fake values on a new inode */ 13562306a36Sopenharmony_ci if (inode->i_state & I_NEW) { 13662306a36Sopenharmony_ci if (fattr->cf_cifsattrs & ATTR_DIRECTORY) 13762306a36Sopenharmony_ci set_nlink(inode, 2); 13862306a36Sopenharmony_ci else 13962306a36Sopenharmony_ci set_nlink(inode, 1); 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci return; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* we trust the server, so update it */ 14562306a36Sopenharmony_ci set_nlink(inode, fattr->cf_nlink); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci/* populate an inode with info from a cifs_fattr struct */ 14962306a36Sopenharmony_ciint 15062306a36Sopenharmony_cicifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr, 15162306a36Sopenharmony_ci bool from_readdir) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci struct cifsInodeInfo *cifs_i = CIFS_I(inode); 15462306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (!(inode->i_state & I_NEW) && 15762306a36Sopenharmony_ci unlikely(inode_wrong_type(inode, fattr->cf_mode))) { 15862306a36Sopenharmony_ci CIFS_I(inode)->time = 0; /* force reval */ 15962306a36Sopenharmony_ci return -ESTALE; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci cifs_revalidate_cache(inode, fattr); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci spin_lock(&inode->i_lock); 16562306a36Sopenharmony_ci fattr->cf_mtime = timestamp_truncate(fattr->cf_mtime, inode); 16662306a36Sopenharmony_ci fattr->cf_atime = timestamp_truncate(fattr->cf_atime, inode); 16762306a36Sopenharmony_ci fattr->cf_ctime = timestamp_truncate(fattr->cf_ctime, inode); 16862306a36Sopenharmony_ci /* we do not want atime to be less than mtime, it broke some apps */ 16962306a36Sopenharmony_ci if (timespec64_compare(&fattr->cf_atime, &fattr->cf_mtime) < 0) 17062306a36Sopenharmony_ci inode_set_atime_to_ts(inode, fattr->cf_mtime); 17162306a36Sopenharmony_ci else 17262306a36Sopenharmony_ci inode_set_atime_to_ts(inode, fattr->cf_atime); 17362306a36Sopenharmony_ci inode_set_mtime_to_ts(inode, fattr->cf_mtime); 17462306a36Sopenharmony_ci inode_set_ctime_to_ts(inode, fattr->cf_ctime); 17562306a36Sopenharmony_ci inode->i_rdev = fattr->cf_rdev; 17662306a36Sopenharmony_ci cifs_nlink_fattr_to_inode(inode, fattr); 17762306a36Sopenharmony_ci inode->i_uid = fattr->cf_uid; 17862306a36Sopenharmony_ci inode->i_gid = fattr->cf_gid; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* if dynperm is set, don't clobber existing mode */ 18162306a36Sopenharmony_ci if (inode->i_state & I_NEW || 18262306a36Sopenharmony_ci !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) 18362306a36Sopenharmony_ci inode->i_mode = fattr->cf_mode; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci cifs_i->cifsAttrs = fattr->cf_cifsattrs; 18662306a36Sopenharmony_ci cifs_i->reparse_tag = fattr->cf_cifstag; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (fattr->cf_flags & CIFS_FATTR_NEED_REVAL) 18962306a36Sopenharmony_ci cifs_i->time = 0; 19062306a36Sopenharmony_ci else 19162306a36Sopenharmony_ci cifs_i->time = jiffies; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (fattr->cf_flags & CIFS_FATTR_DELETE_PENDING) 19462306a36Sopenharmony_ci set_bit(CIFS_INO_DELETE_PENDING, &cifs_i->flags); 19562306a36Sopenharmony_ci else 19662306a36Sopenharmony_ci clear_bit(CIFS_INO_DELETE_PENDING, &cifs_i->flags); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci cifs_i->server_eof = fattr->cf_eof; 19962306a36Sopenharmony_ci /* 20062306a36Sopenharmony_ci * Can't safely change the file size here if the client is writing to 20162306a36Sopenharmony_ci * it due to potential races. 20262306a36Sopenharmony_ci */ 20362306a36Sopenharmony_ci if (is_size_safe_to_change(cifs_i, fattr->cf_eof, from_readdir)) { 20462306a36Sopenharmony_ci i_size_write(inode, fattr->cf_eof); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci /* 20762306a36Sopenharmony_ci * i_blocks is not related to (i_size / i_blksize), 20862306a36Sopenharmony_ci * but instead 512 byte (2**9) size is required for 20962306a36Sopenharmony_ci * calculating num blocks. 21062306a36Sopenharmony_ci */ 21162306a36Sopenharmony_ci inode->i_blocks = (512 - 1 + fattr->cf_bytes) >> 9; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (S_ISLNK(fattr->cf_mode) && fattr->cf_symlink_target) { 21562306a36Sopenharmony_ci kfree(cifs_i->symlink_target); 21662306a36Sopenharmony_ci cifs_i->symlink_target = fattr->cf_symlink_target; 21762306a36Sopenharmony_ci fattr->cf_symlink_target = NULL; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci spin_unlock(&inode->i_lock); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci if (fattr->cf_flags & CIFS_FATTR_JUNCTION) 22262306a36Sopenharmony_ci inode->i_flags |= S_AUTOMOUNT; 22362306a36Sopenharmony_ci if (inode->i_state & I_NEW) 22462306a36Sopenharmony_ci cifs_set_ops(inode); 22562306a36Sopenharmony_ci return 0; 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_civoid 22962306a36Sopenharmony_cicifs_fill_uniqueid(struct super_block *sb, struct cifs_fattr *fattr) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb = CIFS_SB(sb); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) 23462306a36Sopenharmony_ci return; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci fattr->cf_uniqueid = iunique(sb, ROOT_I); 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci/* Fill a cifs_fattr struct with info from FILE_UNIX_BASIC_INFO. */ 24062306a36Sopenharmony_civoid 24162306a36Sopenharmony_cicifs_unix_basic_to_fattr(struct cifs_fattr *fattr, FILE_UNIX_BASIC_INFO *info, 24262306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci memset(fattr, 0, sizeof(*fattr)); 24562306a36Sopenharmony_ci fattr->cf_uniqueid = le64_to_cpu(info->UniqueId); 24662306a36Sopenharmony_ci fattr->cf_bytes = le64_to_cpu(info->NumOfBytes); 24762306a36Sopenharmony_ci fattr->cf_eof = le64_to_cpu(info->EndOfFile); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime); 25062306a36Sopenharmony_ci fattr->cf_mtime = cifs_NTtimeToUnix(info->LastModificationTime); 25162306a36Sopenharmony_ci fattr->cf_ctime = cifs_NTtimeToUnix(info->LastStatusChange); 25262306a36Sopenharmony_ci /* old POSIX extensions don't get create time */ 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci fattr->cf_mode = le64_to_cpu(info->Permissions); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* 25762306a36Sopenharmony_ci * Since we set the inode type below we need to mask off 25862306a36Sopenharmony_ci * to avoid strange results if bits set above. 25962306a36Sopenharmony_ci */ 26062306a36Sopenharmony_ci fattr->cf_mode &= ~S_IFMT; 26162306a36Sopenharmony_ci switch (le32_to_cpu(info->Type)) { 26262306a36Sopenharmony_ci case UNIX_FILE: 26362306a36Sopenharmony_ci fattr->cf_mode |= S_IFREG; 26462306a36Sopenharmony_ci fattr->cf_dtype = DT_REG; 26562306a36Sopenharmony_ci break; 26662306a36Sopenharmony_ci case UNIX_SYMLINK: 26762306a36Sopenharmony_ci fattr->cf_mode |= S_IFLNK; 26862306a36Sopenharmony_ci fattr->cf_dtype = DT_LNK; 26962306a36Sopenharmony_ci break; 27062306a36Sopenharmony_ci case UNIX_DIR: 27162306a36Sopenharmony_ci fattr->cf_mode |= S_IFDIR; 27262306a36Sopenharmony_ci fattr->cf_dtype = DT_DIR; 27362306a36Sopenharmony_ci break; 27462306a36Sopenharmony_ci case UNIX_CHARDEV: 27562306a36Sopenharmony_ci fattr->cf_mode |= S_IFCHR; 27662306a36Sopenharmony_ci fattr->cf_dtype = DT_CHR; 27762306a36Sopenharmony_ci fattr->cf_rdev = MKDEV(le64_to_cpu(info->DevMajor), 27862306a36Sopenharmony_ci le64_to_cpu(info->DevMinor) & MINORMASK); 27962306a36Sopenharmony_ci break; 28062306a36Sopenharmony_ci case UNIX_BLOCKDEV: 28162306a36Sopenharmony_ci fattr->cf_mode |= S_IFBLK; 28262306a36Sopenharmony_ci fattr->cf_dtype = DT_BLK; 28362306a36Sopenharmony_ci fattr->cf_rdev = MKDEV(le64_to_cpu(info->DevMajor), 28462306a36Sopenharmony_ci le64_to_cpu(info->DevMinor) & MINORMASK); 28562306a36Sopenharmony_ci break; 28662306a36Sopenharmony_ci case UNIX_FIFO: 28762306a36Sopenharmony_ci fattr->cf_mode |= S_IFIFO; 28862306a36Sopenharmony_ci fattr->cf_dtype = DT_FIFO; 28962306a36Sopenharmony_ci break; 29062306a36Sopenharmony_ci case UNIX_SOCKET: 29162306a36Sopenharmony_ci fattr->cf_mode |= S_IFSOCK; 29262306a36Sopenharmony_ci fattr->cf_dtype = DT_SOCK; 29362306a36Sopenharmony_ci break; 29462306a36Sopenharmony_ci default: 29562306a36Sopenharmony_ci /* safest to call it a file if we do not know */ 29662306a36Sopenharmony_ci fattr->cf_mode |= S_IFREG; 29762306a36Sopenharmony_ci fattr->cf_dtype = DT_REG; 29862306a36Sopenharmony_ci cifs_dbg(FYI, "unknown type %d\n", le32_to_cpu(info->Type)); 29962306a36Sopenharmony_ci break; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci fattr->cf_uid = cifs_sb->ctx->linux_uid; 30362306a36Sopenharmony_ci if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)) { 30462306a36Sopenharmony_ci u64 id = le64_to_cpu(info->Uid); 30562306a36Sopenharmony_ci if (id < ((uid_t)-1)) { 30662306a36Sopenharmony_ci kuid_t uid = make_kuid(&init_user_ns, id); 30762306a36Sopenharmony_ci if (uid_valid(uid)) 30862306a36Sopenharmony_ci fattr->cf_uid = uid; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci fattr->cf_gid = cifs_sb->ctx->linux_gid; 31362306a36Sopenharmony_ci if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)) { 31462306a36Sopenharmony_ci u64 id = le64_to_cpu(info->Gid); 31562306a36Sopenharmony_ci if (id < ((gid_t)-1)) { 31662306a36Sopenharmony_ci kgid_t gid = make_kgid(&init_user_ns, id); 31762306a36Sopenharmony_ci if (gid_valid(gid)) 31862306a36Sopenharmony_ci fattr->cf_gid = gid; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci fattr->cf_nlink = le64_to_cpu(info->Nlinks); 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci/* 32662306a36Sopenharmony_ci * Fill a cifs_fattr struct with fake inode info. 32762306a36Sopenharmony_ci * 32862306a36Sopenharmony_ci * Needed to setup cifs_fattr data for the directory which is the 32962306a36Sopenharmony_ci * junction to the new submount (ie to setup the fake directory 33062306a36Sopenharmony_ci * which represents a DFS referral or reparse mount point). 33162306a36Sopenharmony_ci */ 33262306a36Sopenharmony_cistatic void cifs_create_junction_fattr(struct cifs_fattr *fattr, 33362306a36Sopenharmony_ci struct super_block *sb) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb = CIFS_SB(sb); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci cifs_dbg(FYI, "%s: creating fake fattr\n", __func__); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci memset(fattr, 0, sizeof(*fattr)); 34062306a36Sopenharmony_ci fattr->cf_mode = S_IFDIR | S_IXUGO | S_IRWXU; 34162306a36Sopenharmony_ci fattr->cf_uid = cifs_sb->ctx->linux_uid; 34262306a36Sopenharmony_ci fattr->cf_gid = cifs_sb->ctx->linux_gid; 34362306a36Sopenharmony_ci ktime_get_coarse_real_ts64(&fattr->cf_mtime); 34462306a36Sopenharmony_ci fattr->cf_atime = fattr->cf_ctime = fattr->cf_mtime; 34562306a36Sopenharmony_ci fattr->cf_nlink = 2; 34662306a36Sopenharmony_ci fattr->cf_flags = CIFS_FATTR_JUNCTION; 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci/* Update inode with final fattr data */ 35062306a36Sopenharmony_cistatic int update_inode_info(struct super_block *sb, 35162306a36Sopenharmony_ci struct cifs_fattr *fattr, 35262306a36Sopenharmony_ci struct inode **inode) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb = CIFS_SB(sb); 35562306a36Sopenharmony_ci int rc = 0; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (!*inode) { 35862306a36Sopenharmony_ci *inode = cifs_iget(sb, fattr); 35962306a36Sopenharmony_ci if (!*inode) 36062306a36Sopenharmony_ci rc = -ENOMEM; 36162306a36Sopenharmony_ci return rc; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci /* We already have inode, update it. 36462306a36Sopenharmony_ci * 36562306a36Sopenharmony_ci * If file type or uniqueid is different, return error. 36662306a36Sopenharmony_ci */ 36762306a36Sopenharmony_ci if (unlikely((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) && 36862306a36Sopenharmony_ci CIFS_I(*inode)->uniqueid != fattr->cf_uniqueid)) { 36962306a36Sopenharmony_ci CIFS_I(*inode)->time = 0; /* force reval */ 37062306a36Sopenharmony_ci return -ESTALE; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci return cifs_fattr_to_inode(*inode, fattr, false); 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY 37662306a36Sopenharmony_cistatic int 37762306a36Sopenharmony_cicifs_get_file_info_unix(struct file *filp) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci int rc; 38062306a36Sopenharmony_ci unsigned int xid; 38162306a36Sopenharmony_ci FILE_UNIX_BASIC_INFO find_data; 38262306a36Sopenharmony_ci struct cifs_fattr fattr = {}; 38362306a36Sopenharmony_ci struct inode *inode = file_inode(filp); 38462306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); 38562306a36Sopenharmony_ci struct cifsFileInfo *cfile = filp->private_data; 38662306a36Sopenharmony_ci struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci xid = get_xid(); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (cfile->symlink_target) { 39162306a36Sopenharmony_ci fattr.cf_symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL); 39262306a36Sopenharmony_ci if (!fattr.cf_symlink_target) { 39362306a36Sopenharmony_ci rc = -ENOMEM; 39462306a36Sopenharmony_ci goto cifs_gfiunix_out; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci rc = CIFSSMBUnixQFileInfo(xid, tcon, cfile->fid.netfid, &find_data); 39962306a36Sopenharmony_ci if (!rc) { 40062306a36Sopenharmony_ci cifs_unix_basic_to_fattr(&fattr, &find_data, cifs_sb); 40162306a36Sopenharmony_ci } else if (rc == -EREMOTE) { 40262306a36Sopenharmony_ci cifs_create_junction_fattr(&fattr, inode->i_sb); 40362306a36Sopenharmony_ci rc = 0; 40462306a36Sopenharmony_ci } else 40562306a36Sopenharmony_ci goto cifs_gfiunix_out; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci rc = cifs_fattr_to_inode(inode, &fattr, false); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cicifs_gfiunix_out: 41062306a36Sopenharmony_ci free_xid(xid); 41162306a36Sopenharmony_ci return rc; 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_cistatic int cifs_get_unix_fattr(const unsigned char *full_path, 41562306a36Sopenharmony_ci struct super_block *sb, 41662306a36Sopenharmony_ci struct cifs_fattr *fattr, 41762306a36Sopenharmony_ci struct inode **pinode, 41862306a36Sopenharmony_ci const unsigned int xid) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci struct TCP_Server_Info *server; 42162306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb = CIFS_SB(sb); 42262306a36Sopenharmony_ci FILE_UNIX_BASIC_INFO find_data; 42362306a36Sopenharmony_ci struct cifs_tcon *tcon; 42462306a36Sopenharmony_ci struct tcon_link *tlink; 42562306a36Sopenharmony_ci int rc, tmprc; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci cifs_dbg(FYI, "Getting info on %s\n", full_path); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci tlink = cifs_sb_tlink(cifs_sb); 43062306a36Sopenharmony_ci if (IS_ERR(tlink)) 43162306a36Sopenharmony_ci return PTR_ERR(tlink); 43262306a36Sopenharmony_ci tcon = tlink_tcon(tlink); 43362306a36Sopenharmony_ci server = tcon->ses->server; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci /* could have done a find first instead but this returns more info */ 43662306a36Sopenharmony_ci rc = CIFSSMBUnixQPathInfo(xid, tcon, full_path, &find_data, 43762306a36Sopenharmony_ci cifs_sb->local_nls, cifs_remap(cifs_sb)); 43862306a36Sopenharmony_ci cifs_dbg(FYI, "%s: query path info: rc = %d\n", __func__, rc); 43962306a36Sopenharmony_ci cifs_put_tlink(tlink); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if (!rc) { 44262306a36Sopenharmony_ci cifs_unix_basic_to_fattr(fattr, &find_data, cifs_sb); 44362306a36Sopenharmony_ci } else if (rc == -EREMOTE) { 44462306a36Sopenharmony_ci cifs_create_junction_fattr(fattr, sb); 44562306a36Sopenharmony_ci rc = 0; 44662306a36Sopenharmony_ci } else { 44762306a36Sopenharmony_ci return rc; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci if (!*pinode) 45162306a36Sopenharmony_ci cifs_fill_uniqueid(sb, fattr); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci /* check for Minshall+French symlinks */ 45462306a36Sopenharmony_ci if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) { 45562306a36Sopenharmony_ci tmprc = check_mf_symlink(xid, tcon, cifs_sb, fattr, full_path); 45662306a36Sopenharmony_ci cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc); 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (S_ISLNK(fattr->cf_mode) && !fattr->cf_symlink_target) { 46062306a36Sopenharmony_ci if (!server->ops->query_symlink) 46162306a36Sopenharmony_ci return -EOPNOTSUPP; 46262306a36Sopenharmony_ci rc = server->ops->query_symlink(xid, tcon, 46362306a36Sopenharmony_ci cifs_sb, full_path, 46462306a36Sopenharmony_ci &fattr->cf_symlink_target); 46562306a36Sopenharmony_ci cifs_dbg(FYI, "%s: query_symlink: %d\n", __func__, rc); 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci return rc; 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ciint cifs_get_inode_info_unix(struct inode **pinode, 47162306a36Sopenharmony_ci const unsigned char *full_path, 47262306a36Sopenharmony_ci struct super_block *sb, unsigned int xid) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci struct cifs_fattr fattr = {}; 47562306a36Sopenharmony_ci int rc; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci rc = cifs_get_unix_fattr(full_path, sb, &fattr, pinode, xid); 47862306a36Sopenharmony_ci if (rc) 47962306a36Sopenharmony_ci goto out; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci rc = update_inode_info(sb, &fattr, pinode); 48262306a36Sopenharmony_ciout: 48362306a36Sopenharmony_ci kfree(fattr.cf_symlink_target); 48462306a36Sopenharmony_ci return rc; 48562306a36Sopenharmony_ci} 48662306a36Sopenharmony_ci#else 48762306a36Sopenharmony_cistatic inline int cifs_get_unix_fattr(const unsigned char *full_path, 48862306a36Sopenharmony_ci struct super_block *sb, 48962306a36Sopenharmony_ci struct cifs_fattr *fattr, 49062306a36Sopenharmony_ci struct inode **pinode, 49162306a36Sopenharmony_ci const unsigned int xid) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci return -EOPNOTSUPP; 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ciint cifs_get_inode_info_unix(struct inode **pinode, 49762306a36Sopenharmony_ci const unsigned char *full_path, 49862306a36Sopenharmony_ci struct super_block *sb, unsigned int xid) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci return -EOPNOTSUPP; 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_cistatic int 50562306a36Sopenharmony_cicifs_sfu_type(struct cifs_fattr *fattr, const char *path, 50662306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb, unsigned int xid) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci int rc; 50962306a36Sopenharmony_ci __u32 oplock; 51062306a36Sopenharmony_ci struct tcon_link *tlink; 51162306a36Sopenharmony_ci struct cifs_tcon *tcon; 51262306a36Sopenharmony_ci struct cifs_fid fid; 51362306a36Sopenharmony_ci struct cifs_open_parms oparms; 51462306a36Sopenharmony_ci struct cifs_io_parms io_parms = {0}; 51562306a36Sopenharmony_ci char buf[24]; 51662306a36Sopenharmony_ci unsigned int bytes_read; 51762306a36Sopenharmony_ci char *pbuf; 51862306a36Sopenharmony_ci int buf_type = CIFS_NO_BUFFER; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci pbuf = buf; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci fattr->cf_mode &= ~S_IFMT; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci if (fattr->cf_eof == 0) { 52562306a36Sopenharmony_ci fattr->cf_mode |= S_IFIFO; 52662306a36Sopenharmony_ci fattr->cf_dtype = DT_FIFO; 52762306a36Sopenharmony_ci return 0; 52862306a36Sopenharmony_ci } else if (fattr->cf_eof < 8) { 52962306a36Sopenharmony_ci fattr->cf_mode |= S_IFREG; 53062306a36Sopenharmony_ci fattr->cf_dtype = DT_REG; 53162306a36Sopenharmony_ci return -EINVAL; /* EOPNOTSUPP? */ 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci tlink = cifs_sb_tlink(cifs_sb); 53562306a36Sopenharmony_ci if (IS_ERR(tlink)) 53662306a36Sopenharmony_ci return PTR_ERR(tlink); 53762306a36Sopenharmony_ci tcon = tlink_tcon(tlink); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci oparms = (struct cifs_open_parms) { 54062306a36Sopenharmony_ci .tcon = tcon, 54162306a36Sopenharmony_ci .cifs_sb = cifs_sb, 54262306a36Sopenharmony_ci .desired_access = GENERIC_READ, 54362306a36Sopenharmony_ci .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), 54462306a36Sopenharmony_ci .disposition = FILE_OPEN, 54562306a36Sopenharmony_ci .path = path, 54662306a36Sopenharmony_ci .fid = &fid, 54762306a36Sopenharmony_ci }; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci if (tcon->ses->server->oplocks) 55062306a36Sopenharmony_ci oplock = REQ_OPLOCK; 55162306a36Sopenharmony_ci else 55262306a36Sopenharmony_ci oplock = 0; 55362306a36Sopenharmony_ci rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, NULL); 55462306a36Sopenharmony_ci if (rc) { 55562306a36Sopenharmony_ci cifs_dbg(FYI, "check sfu type of %s, open rc = %d\n", path, rc); 55662306a36Sopenharmony_ci cifs_put_tlink(tlink); 55762306a36Sopenharmony_ci return rc; 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci /* Read header */ 56162306a36Sopenharmony_ci io_parms.netfid = fid.netfid; 56262306a36Sopenharmony_ci io_parms.pid = current->tgid; 56362306a36Sopenharmony_ci io_parms.tcon = tcon; 56462306a36Sopenharmony_ci io_parms.offset = 0; 56562306a36Sopenharmony_ci io_parms.length = 24; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci rc = tcon->ses->server->ops->sync_read(xid, &fid, &io_parms, 56862306a36Sopenharmony_ci &bytes_read, &pbuf, &buf_type); 56962306a36Sopenharmony_ci if ((rc == 0) && (bytes_read >= 8)) { 57062306a36Sopenharmony_ci if (memcmp("IntxBLK", pbuf, 8) == 0) { 57162306a36Sopenharmony_ci cifs_dbg(FYI, "Block device\n"); 57262306a36Sopenharmony_ci fattr->cf_mode |= S_IFBLK; 57362306a36Sopenharmony_ci fattr->cf_dtype = DT_BLK; 57462306a36Sopenharmony_ci if (bytes_read == 24) { 57562306a36Sopenharmony_ci /* we have enough to decode dev num */ 57662306a36Sopenharmony_ci __u64 mjr; /* major */ 57762306a36Sopenharmony_ci __u64 mnr; /* minor */ 57862306a36Sopenharmony_ci mjr = le64_to_cpu(*(__le64 *)(pbuf+8)); 57962306a36Sopenharmony_ci mnr = le64_to_cpu(*(__le64 *)(pbuf+16)); 58062306a36Sopenharmony_ci fattr->cf_rdev = MKDEV(mjr, mnr); 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci } else if (memcmp("IntxCHR", pbuf, 8) == 0) { 58362306a36Sopenharmony_ci cifs_dbg(FYI, "Char device\n"); 58462306a36Sopenharmony_ci fattr->cf_mode |= S_IFCHR; 58562306a36Sopenharmony_ci fattr->cf_dtype = DT_CHR; 58662306a36Sopenharmony_ci if (bytes_read == 24) { 58762306a36Sopenharmony_ci /* we have enough to decode dev num */ 58862306a36Sopenharmony_ci __u64 mjr; /* major */ 58962306a36Sopenharmony_ci __u64 mnr; /* minor */ 59062306a36Sopenharmony_ci mjr = le64_to_cpu(*(__le64 *)(pbuf+8)); 59162306a36Sopenharmony_ci mnr = le64_to_cpu(*(__le64 *)(pbuf+16)); 59262306a36Sopenharmony_ci fattr->cf_rdev = MKDEV(mjr, mnr); 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci } else if (memcmp("IntxLNK", pbuf, 7) == 0) { 59562306a36Sopenharmony_ci cifs_dbg(FYI, "Symlink\n"); 59662306a36Sopenharmony_ci fattr->cf_mode |= S_IFLNK; 59762306a36Sopenharmony_ci fattr->cf_dtype = DT_LNK; 59862306a36Sopenharmony_ci } else if (memcmp("LnxFIFO", pbuf, 8) == 0) { 59962306a36Sopenharmony_ci cifs_dbg(FYI, "FIFO\n"); 60062306a36Sopenharmony_ci fattr->cf_mode |= S_IFIFO; 60162306a36Sopenharmony_ci fattr->cf_dtype = DT_FIFO; 60262306a36Sopenharmony_ci } else { 60362306a36Sopenharmony_ci fattr->cf_mode |= S_IFREG; /* file? */ 60462306a36Sopenharmony_ci fattr->cf_dtype = DT_REG; 60562306a36Sopenharmony_ci rc = -EOPNOTSUPP; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci } else { 60862306a36Sopenharmony_ci fattr->cf_mode |= S_IFREG; /* then it is a file */ 60962306a36Sopenharmony_ci fattr->cf_dtype = DT_REG; 61062306a36Sopenharmony_ci rc = -EOPNOTSUPP; /* or some unknown SFU type */ 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci tcon->ses->server->ops->close(xid, tcon, &fid); 61462306a36Sopenharmony_ci cifs_put_tlink(tlink); 61562306a36Sopenharmony_ci return rc; 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci#define SFBITS_MASK (S_ISVTX | S_ISGID | S_ISUID) /* SETFILEBITS valid bits */ 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci/* 62162306a36Sopenharmony_ci * Fetch mode bits as provided by SFU. 62262306a36Sopenharmony_ci * 62362306a36Sopenharmony_ci * FIXME: Doesn't this clobber the type bit we got from cifs_sfu_type ? 62462306a36Sopenharmony_ci */ 62562306a36Sopenharmony_cistatic int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path, 62662306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb, unsigned int xid) 62762306a36Sopenharmony_ci{ 62862306a36Sopenharmony_ci#ifdef CONFIG_CIFS_XATTR 62962306a36Sopenharmony_ci ssize_t rc; 63062306a36Sopenharmony_ci char ea_value[4]; 63162306a36Sopenharmony_ci __u32 mode; 63262306a36Sopenharmony_ci struct tcon_link *tlink; 63362306a36Sopenharmony_ci struct cifs_tcon *tcon; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci tlink = cifs_sb_tlink(cifs_sb); 63662306a36Sopenharmony_ci if (IS_ERR(tlink)) 63762306a36Sopenharmony_ci return PTR_ERR(tlink); 63862306a36Sopenharmony_ci tcon = tlink_tcon(tlink); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci if (tcon->ses->server->ops->query_all_EAs == NULL) { 64162306a36Sopenharmony_ci cifs_put_tlink(tlink); 64262306a36Sopenharmony_ci return -EOPNOTSUPP; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci rc = tcon->ses->server->ops->query_all_EAs(xid, tcon, path, 64662306a36Sopenharmony_ci "SETFILEBITS", ea_value, 4 /* size of buf */, 64762306a36Sopenharmony_ci cifs_sb); 64862306a36Sopenharmony_ci cifs_put_tlink(tlink); 64962306a36Sopenharmony_ci if (rc < 0) 65062306a36Sopenharmony_ci return (int)rc; 65162306a36Sopenharmony_ci else if (rc > 3) { 65262306a36Sopenharmony_ci mode = le32_to_cpu(*((__le32 *)ea_value)); 65362306a36Sopenharmony_ci fattr->cf_mode &= ~SFBITS_MASK; 65462306a36Sopenharmony_ci cifs_dbg(FYI, "special bits 0%o org mode 0%o\n", 65562306a36Sopenharmony_ci mode, fattr->cf_mode); 65662306a36Sopenharmony_ci fattr->cf_mode = (mode & SFBITS_MASK) | fattr->cf_mode; 65762306a36Sopenharmony_ci cifs_dbg(FYI, "special mode bits 0%o\n", mode); 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci return 0; 66162306a36Sopenharmony_ci#else 66262306a36Sopenharmony_ci return -EOPNOTSUPP; 66362306a36Sopenharmony_ci#endif 66462306a36Sopenharmony_ci} 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci/* Fill a cifs_fattr struct with info from POSIX info struct */ 66762306a36Sopenharmony_cistatic void smb311_posix_info_to_fattr(struct cifs_fattr *fattr, 66862306a36Sopenharmony_ci struct cifs_open_info_data *data, 66962306a36Sopenharmony_ci struct cifs_sid *owner, 67062306a36Sopenharmony_ci struct cifs_sid *group, 67162306a36Sopenharmony_ci struct super_block *sb) 67262306a36Sopenharmony_ci{ 67362306a36Sopenharmony_ci struct smb311_posix_qinfo *info = &data->posix_fi; 67462306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb = CIFS_SB(sb); 67562306a36Sopenharmony_ci struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci memset(fattr, 0, sizeof(*fattr)); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci /* no fattr->flags to set */ 68062306a36Sopenharmony_ci fattr->cf_cifsattrs = le32_to_cpu(info->DosAttributes); 68162306a36Sopenharmony_ci fattr->cf_uniqueid = le64_to_cpu(info->Inode); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci if (info->LastAccessTime) 68462306a36Sopenharmony_ci fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime); 68562306a36Sopenharmony_ci else 68662306a36Sopenharmony_ci ktime_get_coarse_real_ts64(&fattr->cf_atime); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime); 68962306a36Sopenharmony_ci fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci if (data->adjust_tz) { 69262306a36Sopenharmony_ci fattr->cf_ctime.tv_sec += tcon->ses->server->timeAdj; 69362306a36Sopenharmony_ci fattr->cf_mtime.tv_sec += tcon->ses->server->timeAdj; 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci fattr->cf_eof = le64_to_cpu(info->EndOfFile); 69762306a36Sopenharmony_ci fattr->cf_bytes = le64_to_cpu(info->AllocationSize); 69862306a36Sopenharmony_ci fattr->cf_createtime = le64_to_cpu(info->CreationTime); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci fattr->cf_nlink = le32_to_cpu(info->HardLinks); 70162306a36Sopenharmony_ci fattr->cf_mode = (umode_t) le32_to_cpu(info->Mode); 70262306a36Sopenharmony_ci /* The srv fs device id is overridden on network mount so setting rdev isn't needed here */ 70362306a36Sopenharmony_ci /* fattr->cf_rdev = le32_to_cpu(info->DeviceId); */ 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci if (data->symlink) { 70662306a36Sopenharmony_ci fattr->cf_mode |= S_IFLNK; 70762306a36Sopenharmony_ci fattr->cf_dtype = DT_LNK; 70862306a36Sopenharmony_ci fattr->cf_symlink_target = data->symlink_target; 70962306a36Sopenharmony_ci data->symlink_target = NULL; 71062306a36Sopenharmony_ci } else if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { 71162306a36Sopenharmony_ci fattr->cf_mode |= S_IFDIR; 71262306a36Sopenharmony_ci fattr->cf_dtype = DT_DIR; 71362306a36Sopenharmony_ci } else { /* file */ 71462306a36Sopenharmony_ci fattr->cf_mode |= S_IFREG; 71562306a36Sopenharmony_ci fattr->cf_dtype = DT_REG; 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci /* else if reparse point ... TODO: add support for FIFO and blk dev; special file types */ 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci sid_to_id(cifs_sb, owner, fattr, SIDOWNER); 72062306a36Sopenharmony_ci sid_to_id(cifs_sb, group, fattr, SIDGROUP); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci cifs_dbg(FYI, "POSIX query info: mode 0x%x uniqueid 0x%llx nlink %d\n", 72362306a36Sopenharmony_ci fattr->cf_mode, fattr->cf_uniqueid, fattr->cf_nlink); 72462306a36Sopenharmony_ci} 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_cistatic inline dev_t nfs_mkdev(struct reparse_posix_data *buf) 72762306a36Sopenharmony_ci{ 72862306a36Sopenharmony_ci u64 v = le64_to_cpu(*(__le64 *)buf->DataBuffer); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci return MKDEV(v >> 32, v & 0xffffffff); 73162306a36Sopenharmony_ci} 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_cibool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb, 73462306a36Sopenharmony_ci struct cifs_fattr *fattr, 73562306a36Sopenharmony_ci struct cifs_open_info_data *data) 73662306a36Sopenharmony_ci{ 73762306a36Sopenharmony_ci struct reparse_posix_data *buf = data->reparse.posix; 73862306a36Sopenharmony_ci u32 tag = data->reparse.tag; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci if (tag == IO_REPARSE_TAG_NFS && buf) { 74162306a36Sopenharmony_ci switch (le64_to_cpu(buf->InodeType)) { 74262306a36Sopenharmony_ci case NFS_SPECFILE_CHR: 74362306a36Sopenharmony_ci fattr->cf_mode |= S_IFCHR | cifs_sb->ctx->file_mode; 74462306a36Sopenharmony_ci fattr->cf_dtype = DT_CHR; 74562306a36Sopenharmony_ci fattr->cf_rdev = nfs_mkdev(buf); 74662306a36Sopenharmony_ci break; 74762306a36Sopenharmony_ci case NFS_SPECFILE_BLK: 74862306a36Sopenharmony_ci fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode; 74962306a36Sopenharmony_ci fattr->cf_dtype = DT_BLK; 75062306a36Sopenharmony_ci fattr->cf_rdev = nfs_mkdev(buf); 75162306a36Sopenharmony_ci break; 75262306a36Sopenharmony_ci case NFS_SPECFILE_FIFO: 75362306a36Sopenharmony_ci fattr->cf_mode |= S_IFIFO | cifs_sb->ctx->file_mode; 75462306a36Sopenharmony_ci fattr->cf_dtype = DT_FIFO; 75562306a36Sopenharmony_ci break; 75662306a36Sopenharmony_ci case NFS_SPECFILE_SOCK: 75762306a36Sopenharmony_ci fattr->cf_mode |= S_IFSOCK | cifs_sb->ctx->file_mode; 75862306a36Sopenharmony_ci fattr->cf_dtype = DT_SOCK; 75962306a36Sopenharmony_ci break; 76062306a36Sopenharmony_ci case NFS_SPECFILE_LNK: 76162306a36Sopenharmony_ci fattr->cf_mode = S_IFLNK | cifs_sb->ctx->file_mode; 76262306a36Sopenharmony_ci fattr->cf_dtype = DT_LNK; 76362306a36Sopenharmony_ci break; 76462306a36Sopenharmony_ci default: 76562306a36Sopenharmony_ci WARN_ON_ONCE(1); 76662306a36Sopenharmony_ci return false; 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci return true; 76962306a36Sopenharmony_ci } 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci switch (tag) { 77262306a36Sopenharmony_ci case IO_REPARSE_TAG_LX_SYMLINK: 77362306a36Sopenharmony_ci fattr->cf_mode |= S_IFLNK | cifs_sb->ctx->file_mode; 77462306a36Sopenharmony_ci fattr->cf_dtype = DT_LNK; 77562306a36Sopenharmony_ci break; 77662306a36Sopenharmony_ci case IO_REPARSE_TAG_LX_FIFO: 77762306a36Sopenharmony_ci fattr->cf_mode |= S_IFIFO | cifs_sb->ctx->file_mode; 77862306a36Sopenharmony_ci fattr->cf_dtype = DT_FIFO; 77962306a36Sopenharmony_ci break; 78062306a36Sopenharmony_ci case IO_REPARSE_TAG_AF_UNIX: 78162306a36Sopenharmony_ci fattr->cf_mode |= S_IFSOCK | cifs_sb->ctx->file_mode; 78262306a36Sopenharmony_ci fattr->cf_dtype = DT_SOCK; 78362306a36Sopenharmony_ci break; 78462306a36Sopenharmony_ci case IO_REPARSE_TAG_LX_CHR: 78562306a36Sopenharmony_ci fattr->cf_mode |= S_IFCHR | cifs_sb->ctx->file_mode; 78662306a36Sopenharmony_ci fattr->cf_dtype = DT_CHR; 78762306a36Sopenharmony_ci break; 78862306a36Sopenharmony_ci case IO_REPARSE_TAG_LX_BLK: 78962306a36Sopenharmony_ci fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode; 79062306a36Sopenharmony_ci fattr->cf_dtype = DT_BLK; 79162306a36Sopenharmony_ci break; 79262306a36Sopenharmony_ci case 0: /* SMB1 symlink */ 79362306a36Sopenharmony_ci case IO_REPARSE_TAG_SYMLINK: 79462306a36Sopenharmony_ci case IO_REPARSE_TAG_NFS: 79562306a36Sopenharmony_ci fattr->cf_mode = S_IFLNK | cifs_sb->ctx->file_mode; 79662306a36Sopenharmony_ci fattr->cf_dtype = DT_LNK; 79762306a36Sopenharmony_ci break; 79862306a36Sopenharmony_ci default: 79962306a36Sopenharmony_ci return false; 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci return true; 80262306a36Sopenharmony_ci} 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_cistatic void cifs_open_info_to_fattr(struct cifs_fattr *fattr, 80562306a36Sopenharmony_ci struct cifs_open_info_data *data, 80662306a36Sopenharmony_ci struct super_block *sb) 80762306a36Sopenharmony_ci{ 80862306a36Sopenharmony_ci struct smb2_file_all_info *info = &data->fi; 80962306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb = CIFS_SB(sb); 81062306a36Sopenharmony_ci struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci memset(fattr, 0, sizeof(*fattr)); 81362306a36Sopenharmony_ci fattr->cf_cifsattrs = le32_to_cpu(info->Attributes); 81462306a36Sopenharmony_ci if (info->DeletePending) 81562306a36Sopenharmony_ci fattr->cf_flags |= CIFS_FATTR_DELETE_PENDING; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci if (info->LastAccessTime) 81862306a36Sopenharmony_ci fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime); 81962306a36Sopenharmony_ci else 82062306a36Sopenharmony_ci ktime_get_coarse_real_ts64(&fattr->cf_atime); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime); 82362306a36Sopenharmony_ci fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci if (data->adjust_tz) { 82662306a36Sopenharmony_ci fattr->cf_ctime.tv_sec += tcon->ses->server->timeAdj; 82762306a36Sopenharmony_ci fattr->cf_mtime.tv_sec += tcon->ses->server->timeAdj; 82862306a36Sopenharmony_ci } 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci fattr->cf_eof = le64_to_cpu(info->EndOfFile); 83162306a36Sopenharmony_ci fattr->cf_bytes = le64_to_cpu(info->AllocationSize); 83262306a36Sopenharmony_ci fattr->cf_createtime = le64_to_cpu(info->CreationTime); 83362306a36Sopenharmony_ci fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci if (cifs_open_data_reparse(data) && 83662306a36Sopenharmony_ci cifs_reparse_point_to_fattr(cifs_sb, fattr, data)) 83762306a36Sopenharmony_ci goto out_reparse; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { 84062306a36Sopenharmony_ci fattr->cf_mode = S_IFDIR | cifs_sb->ctx->dir_mode; 84162306a36Sopenharmony_ci fattr->cf_dtype = DT_DIR; 84262306a36Sopenharmony_ci /* 84362306a36Sopenharmony_ci * Server can return wrong NumberOfLinks value for directories 84462306a36Sopenharmony_ci * when Unix extensions are disabled - fake it. 84562306a36Sopenharmony_ci */ 84662306a36Sopenharmony_ci if (!tcon->unix_ext) 84762306a36Sopenharmony_ci fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK; 84862306a36Sopenharmony_ci } else { 84962306a36Sopenharmony_ci fattr->cf_mode = S_IFREG | cifs_sb->ctx->file_mode; 85062306a36Sopenharmony_ci fattr->cf_dtype = DT_REG; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci /* clear write bits if ATTR_READONLY is set */ 85362306a36Sopenharmony_ci if (fattr->cf_cifsattrs & ATTR_READONLY) 85462306a36Sopenharmony_ci fattr->cf_mode &= ~(S_IWUGO); 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci /* 85762306a36Sopenharmony_ci * Don't accept zero nlink from non-unix servers unless 85862306a36Sopenharmony_ci * delete is pending. Instead mark it as unknown. 85962306a36Sopenharmony_ci */ 86062306a36Sopenharmony_ci if ((fattr->cf_nlink < 1) && !tcon->unix_ext && 86162306a36Sopenharmony_ci !info->DeletePending) { 86262306a36Sopenharmony_ci cifs_dbg(VFS, "bogus file nlink value %u\n", 86362306a36Sopenharmony_ci fattr->cf_nlink); 86462306a36Sopenharmony_ci fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK; 86562306a36Sopenharmony_ci } 86662306a36Sopenharmony_ci } 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ciout_reparse: 86962306a36Sopenharmony_ci if (S_ISLNK(fattr->cf_mode)) { 87062306a36Sopenharmony_ci if (likely(data->symlink_target)) 87162306a36Sopenharmony_ci fattr->cf_eof = strnlen(data->symlink_target, PATH_MAX); 87262306a36Sopenharmony_ci fattr->cf_symlink_target = data->symlink_target; 87362306a36Sopenharmony_ci data->symlink_target = NULL; 87462306a36Sopenharmony_ci } 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci fattr->cf_uid = cifs_sb->ctx->linux_uid; 87762306a36Sopenharmony_ci fattr->cf_gid = cifs_sb->ctx->linux_gid; 87862306a36Sopenharmony_ci} 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_cistatic int 88162306a36Sopenharmony_cicifs_get_file_info(struct file *filp) 88262306a36Sopenharmony_ci{ 88362306a36Sopenharmony_ci int rc; 88462306a36Sopenharmony_ci unsigned int xid; 88562306a36Sopenharmony_ci struct cifs_open_info_data data = {}; 88662306a36Sopenharmony_ci struct cifs_fattr fattr; 88762306a36Sopenharmony_ci struct inode *inode = file_inode(filp); 88862306a36Sopenharmony_ci struct cifsFileInfo *cfile = filp->private_data; 88962306a36Sopenharmony_ci struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); 89062306a36Sopenharmony_ci struct TCP_Server_Info *server = tcon->ses->server; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci if (!server->ops->query_file_info) 89362306a36Sopenharmony_ci return -ENOSYS; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci xid = get_xid(); 89662306a36Sopenharmony_ci rc = server->ops->query_file_info(xid, tcon, cfile, &data); 89762306a36Sopenharmony_ci switch (rc) { 89862306a36Sopenharmony_ci case 0: 89962306a36Sopenharmony_ci /* TODO: add support to query reparse tag */ 90062306a36Sopenharmony_ci data.adjust_tz = false; 90162306a36Sopenharmony_ci if (data.symlink_target) { 90262306a36Sopenharmony_ci data.symlink = true; 90362306a36Sopenharmony_ci data.reparse.tag = IO_REPARSE_TAG_SYMLINK; 90462306a36Sopenharmony_ci } 90562306a36Sopenharmony_ci cifs_open_info_to_fattr(&fattr, &data, inode->i_sb); 90662306a36Sopenharmony_ci break; 90762306a36Sopenharmony_ci case -EREMOTE: 90862306a36Sopenharmony_ci cifs_create_junction_fattr(&fattr, inode->i_sb); 90962306a36Sopenharmony_ci rc = 0; 91062306a36Sopenharmony_ci break; 91162306a36Sopenharmony_ci case -EOPNOTSUPP: 91262306a36Sopenharmony_ci case -EINVAL: 91362306a36Sopenharmony_ci /* 91462306a36Sopenharmony_ci * FIXME: legacy server -- fall back to path-based call? 91562306a36Sopenharmony_ci * for now, just skip revalidating and mark inode for 91662306a36Sopenharmony_ci * immediate reval. 91762306a36Sopenharmony_ci */ 91862306a36Sopenharmony_ci rc = 0; 91962306a36Sopenharmony_ci CIFS_I(inode)->time = 0; 92062306a36Sopenharmony_ci goto cgfi_exit; 92162306a36Sopenharmony_ci default: 92262306a36Sopenharmony_ci goto cgfi_exit; 92362306a36Sopenharmony_ci } 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci /* 92662306a36Sopenharmony_ci * don't bother with SFU junk here -- just mark inode as needing 92762306a36Sopenharmony_ci * revalidation. 92862306a36Sopenharmony_ci */ 92962306a36Sopenharmony_ci fattr.cf_uniqueid = CIFS_I(inode)->uniqueid; 93062306a36Sopenharmony_ci fattr.cf_flags |= CIFS_FATTR_NEED_REVAL; 93162306a36Sopenharmony_ci /* if filetype is different, return error */ 93262306a36Sopenharmony_ci rc = cifs_fattr_to_inode(inode, &fattr, false); 93362306a36Sopenharmony_cicgfi_exit: 93462306a36Sopenharmony_ci cifs_free_open_info(&data); 93562306a36Sopenharmony_ci free_xid(xid); 93662306a36Sopenharmony_ci return rc; 93762306a36Sopenharmony_ci} 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci/* Simple function to return a 64 bit hash of string. Rarely called */ 94062306a36Sopenharmony_cistatic __u64 simple_hashstr(const char *str) 94162306a36Sopenharmony_ci{ 94262306a36Sopenharmony_ci const __u64 hash_mult = 1125899906842597ULL; /* a big enough prime */ 94362306a36Sopenharmony_ci __u64 hash = 0; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci while (*str) 94662306a36Sopenharmony_ci hash = (hash + (__u64) *str++) * hash_mult; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci return hash; 94962306a36Sopenharmony_ci} 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY 95262306a36Sopenharmony_ci/** 95362306a36Sopenharmony_ci * cifs_backup_query_path_info - SMB1 fallback code to get ino 95462306a36Sopenharmony_ci * 95562306a36Sopenharmony_ci * Fallback code to get file metadata when we don't have access to 95662306a36Sopenharmony_ci * full_path (EACCES) and have backup creds. 95762306a36Sopenharmony_ci * 95862306a36Sopenharmony_ci * @xid: transaction id used to identify original request in logs 95962306a36Sopenharmony_ci * @tcon: information about the server share we have mounted 96062306a36Sopenharmony_ci * @sb: the superblock stores info such as disk space available 96162306a36Sopenharmony_ci * @full_path: name of the file we are getting the metadata for 96262306a36Sopenharmony_ci * @resp_buf: will be set to cifs resp buf and needs to be freed with 96362306a36Sopenharmony_ci * cifs_buf_release() when done with @data 96462306a36Sopenharmony_ci * @data: will be set to search info result buffer 96562306a36Sopenharmony_ci */ 96662306a36Sopenharmony_cistatic int 96762306a36Sopenharmony_cicifs_backup_query_path_info(int xid, 96862306a36Sopenharmony_ci struct cifs_tcon *tcon, 96962306a36Sopenharmony_ci struct super_block *sb, 97062306a36Sopenharmony_ci const char *full_path, 97162306a36Sopenharmony_ci void **resp_buf, 97262306a36Sopenharmony_ci FILE_ALL_INFO **data) 97362306a36Sopenharmony_ci{ 97462306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb = CIFS_SB(sb); 97562306a36Sopenharmony_ci struct cifs_search_info info = {0}; 97662306a36Sopenharmony_ci u16 flags; 97762306a36Sopenharmony_ci int rc; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci *resp_buf = NULL; 98062306a36Sopenharmony_ci info.endOfSearch = false; 98162306a36Sopenharmony_ci if (tcon->unix_ext) 98262306a36Sopenharmony_ci info.info_level = SMB_FIND_FILE_UNIX; 98362306a36Sopenharmony_ci else if ((tcon->ses->capabilities & 98462306a36Sopenharmony_ci tcon->ses->server->vals->cap_nt_find) == 0) 98562306a36Sopenharmony_ci info.info_level = SMB_FIND_FILE_INFO_STANDARD; 98662306a36Sopenharmony_ci else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) 98762306a36Sopenharmony_ci info.info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO; 98862306a36Sopenharmony_ci else /* no srvino useful for fallback to some netapp */ 98962306a36Sopenharmony_ci info.info_level = SMB_FIND_FILE_DIRECTORY_INFO; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci flags = CIFS_SEARCH_CLOSE_ALWAYS | 99262306a36Sopenharmony_ci CIFS_SEARCH_CLOSE_AT_END | 99362306a36Sopenharmony_ci CIFS_SEARCH_BACKUP_SEARCH; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci rc = CIFSFindFirst(xid, tcon, full_path, 99662306a36Sopenharmony_ci cifs_sb, NULL, flags, &info, false); 99762306a36Sopenharmony_ci if (rc) 99862306a36Sopenharmony_ci return rc; 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci *resp_buf = (void *)info.ntwrk_buf_start; 100162306a36Sopenharmony_ci *data = (FILE_ALL_INFO *)info.srch_entries_start; 100262306a36Sopenharmony_ci return 0; 100362306a36Sopenharmony_ci} 100462306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_cistatic void cifs_set_fattr_ino(int xid, struct cifs_tcon *tcon, struct super_block *sb, 100762306a36Sopenharmony_ci struct inode **inode, const char *full_path, 100862306a36Sopenharmony_ci struct cifs_open_info_data *data, struct cifs_fattr *fattr) 100962306a36Sopenharmony_ci{ 101062306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb = CIFS_SB(sb); 101162306a36Sopenharmony_ci struct TCP_Server_Info *server = tcon->ses->server; 101262306a36Sopenharmony_ci int rc; 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) { 101562306a36Sopenharmony_ci if (*inode) 101662306a36Sopenharmony_ci fattr->cf_uniqueid = CIFS_I(*inode)->uniqueid; 101762306a36Sopenharmony_ci else 101862306a36Sopenharmony_ci fattr->cf_uniqueid = iunique(sb, ROOT_I); 101962306a36Sopenharmony_ci return; 102062306a36Sopenharmony_ci } 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci /* 102362306a36Sopenharmony_ci * If we have an inode pass a NULL tcon to ensure we don't 102462306a36Sopenharmony_ci * make a round trip to the server. This only works for SMB2+. 102562306a36Sopenharmony_ci */ 102662306a36Sopenharmony_ci rc = server->ops->get_srv_inum(xid, *inode ? NULL : tcon, cifs_sb, full_path, 102762306a36Sopenharmony_ci &fattr->cf_uniqueid, data); 102862306a36Sopenharmony_ci if (rc) { 102962306a36Sopenharmony_ci /* 103062306a36Sopenharmony_ci * If that fails reuse existing ino or generate one 103162306a36Sopenharmony_ci * and disable server ones 103262306a36Sopenharmony_ci */ 103362306a36Sopenharmony_ci if (*inode) 103462306a36Sopenharmony_ci fattr->cf_uniqueid = CIFS_I(*inode)->uniqueid; 103562306a36Sopenharmony_ci else { 103662306a36Sopenharmony_ci fattr->cf_uniqueid = iunique(sb, ROOT_I); 103762306a36Sopenharmony_ci cifs_autodisable_serverino(cifs_sb); 103862306a36Sopenharmony_ci } 103962306a36Sopenharmony_ci return; 104062306a36Sopenharmony_ci } 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci /* If no errors, check for zero root inode (invalid) */ 104362306a36Sopenharmony_ci if (fattr->cf_uniqueid == 0 && strlen(full_path) == 0) { 104462306a36Sopenharmony_ci cifs_dbg(FYI, "Invalid (0) inodenum\n"); 104562306a36Sopenharmony_ci if (*inode) { 104662306a36Sopenharmony_ci /* reuse */ 104762306a36Sopenharmony_ci fattr->cf_uniqueid = CIFS_I(*inode)->uniqueid; 104862306a36Sopenharmony_ci } else { 104962306a36Sopenharmony_ci /* make an ino by hashing the UNC */ 105062306a36Sopenharmony_ci fattr->cf_flags |= CIFS_FATTR_FAKE_ROOT_INO; 105162306a36Sopenharmony_ci fattr->cf_uniqueid = simple_hashstr(tcon->tree_name); 105262306a36Sopenharmony_ci } 105362306a36Sopenharmony_ci } 105462306a36Sopenharmony_ci} 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_cistatic inline bool is_inode_cache_good(struct inode *ino) 105762306a36Sopenharmony_ci{ 105862306a36Sopenharmony_ci return ino && CIFS_CACHE_READ(CIFS_I(ino)) && CIFS_I(ino)->time != 0; 105962306a36Sopenharmony_ci} 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_cistatic int reparse_info_to_fattr(struct cifs_open_info_data *data, 106262306a36Sopenharmony_ci struct super_block *sb, 106362306a36Sopenharmony_ci const unsigned int xid, 106462306a36Sopenharmony_ci struct cifs_tcon *tcon, 106562306a36Sopenharmony_ci const char *full_path, 106662306a36Sopenharmony_ci struct cifs_fattr *fattr) 106762306a36Sopenharmony_ci{ 106862306a36Sopenharmony_ci struct TCP_Server_Info *server = tcon->ses->server; 106962306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb = CIFS_SB(sb); 107062306a36Sopenharmony_ci struct kvec rsp_iov, *iov = NULL; 107162306a36Sopenharmony_ci int rsp_buftype = CIFS_NO_BUFFER; 107262306a36Sopenharmony_ci u32 tag = data->reparse.tag; 107362306a36Sopenharmony_ci int rc = 0; 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci if (!tag && server->ops->query_reparse_point) { 107662306a36Sopenharmony_ci rc = server->ops->query_reparse_point(xid, tcon, cifs_sb, 107762306a36Sopenharmony_ci full_path, &tag, 107862306a36Sopenharmony_ci &rsp_iov, &rsp_buftype); 107962306a36Sopenharmony_ci if (!rc) 108062306a36Sopenharmony_ci iov = &rsp_iov; 108162306a36Sopenharmony_ci } 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci rc = -EOPNOTSUPP; 108462306a36Sopenharmony_ci switch ((data->reparse.tag = tag)) { 108562306a36Sopenharmony_ci case 0: /* SMB1 symlink */ 108662306a36Sopenharmony_ci if (server->ops->query_symlink) { 108762306a36Sopenharmony_ci rc = server->ops->query_symlink(xid, tcon, 108862306a36Sopenharmony_ci cifs_sb, full_path, 108962306a36Sopenharmony_ci &data->symlink_target); 109062306a36Sopenharmony_ci } 109162306a36Sopenharmony_ci break; 109262306a36Sopenharmony_ci case IO_REPARSE_TAG_MOUNT_POINT: 109362306a36Sopenharmony_ci cifs_create_junction_fattr(fattr, sb); 109462306a36Sopenharmony_ci rc = 0; 109562306a36Sopenharmony_ci goto out; 109662306a36Sopenharmony_ci default: 109762306a36Sopenharmony_ci if (data->symlink_target) { 109862306a36Sopenharmony_ci rc = 0; 109962306a36Sopenharmony_ci } else if (server->ops->parse_reparse_point) { 110062306a36Sopenharmony_ci rc = server->ops->parse_reparse_point(cifs_sb, 110162306a36Sopenharmony_ci iov, data); 110262306a36Sopenharmony_ci } 110362306a36Sopenharmony_ci break; 110462306a36Sopenharmony_ci } 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci cifs_open_info_to_fattr(fattr, data, sb); 110762306a36Sopenharmony_ciout: 110862306a36Sopenharmony_ci fattr->cf_cifstag = data->reparse.tag; 110962306a36Sopenharmony_ci free_rsp_buf(rsp_buftype, rsp_iov.iov_base); 111062306a36Sopenharmony_ci return rc; 111162306a36Sopenharmony_ci} 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_cistatic int cifs_get_fattr(struct cifs_open_info_data *data, 111462306a36Sopenharmony_ci struct super_block *sb, int xid, 111562306a36Sopenharmony_ci const struct cifs_fid *fid, 111662306a36Sopenharmony_ci struct cifs_fattr *fattr, 111762306a36Sopenharmony_ci struct inode **inode, 111862306a36Sopenharmony_ci const char *full_path) 111962306a36Sopenharmony_ci{ 112062306a36Sopenharmony_ci struct cifs_open_info_data tmp_data = {}; 112162306a36Sopenharmony_ci struct cifs_tcon *tcon; 112262306a36Sopenharmony_ci struct TCP_Server_Info *server; 112362306a36Sopenharmony_ci struct tcon_link *tlink; 112462306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb = CIFS_SB(sb); 112562306a36Sopenharmony_ci void *smb1_backup_rsp_buf = NULL; 112662306a36Sopenharmony_ci int rc = 0; 112762306a36Sopenharmony_ci int tmprc = 0; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci tlink = cifs_sb_tlink(cifs_sb); 113062306a36Sopenharmony_ci if (IS_ERR(tlink)) 113162306a36Sopenharmony_ci return PTR_ERR(tlink); 113262306a36Sopenharmony_ci tcon = tlink_tcon(tlink); 113362306a36Sopenharmony_ci server = tcon->ses->server; 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci /* 113662306a36Sopenharmony_ci * 1. Fetch file metadata if not provided (data) 113762306a36Sopenharmony_ci */ 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci if (!data) { 114062306a36Sopenharmony_ci rc = server->ops->query_path_info(xid, tcon, cifs_sb, 114162306a36Sopenharmony_ci full_path, &tmp_data); 114262306a36Sopenharmony_ci data = &tmp_data; 114362306a36Sopenharmony_ci } 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci /* 114662306a36Sopenharmony_ci * 2. Convert it to internal cifs metadata (fattr) 114762306a36Sopenharmony_ci */ 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci switch (rc) { 115062306a36Sopenharmony_ci case 0: 115162306a36Sopenharmony_ci /* 115262306a36Sopenharmony_ci * If the file is a reparse point, it is more complicated 115362306a36Sopenharmony_ci * since we have to check if its reparse tag matches a known 115462306a36Sopenharmony_ci * special file type e.g. symlink or fifo or char etc. 115562306a36Sopenharmony_ci */ 115662306a36Sopenharmony_ci if (cifs_open_data_reparse(data)) { 115762306a36Sopenharmony_ci rc = reparse_info_to_fattr(data, sb, xid, tcon, 115862306a36Sopenharmony_ci full_path, fattr); 115962306a36Sopenharmony_ci } else { 116062306a36Sopenharmony_ci cifs_open_info_to_fattr(fattr, data, sb); 116162306a36Sopenharmony_ci } 116262306a36Sopenharmony_ci break; 116362306a36Sopenharmony_ci case -EREMOTE: 116462306a36Sopenharmony_ci /* DFS link, no metadata available on this server */ 116562306a36Sopenharmony_ci cifs_create_junction_fattr(fattr, sb); 116662306a36Sopenharmony_ci rc = 0; 116762306a36Sopenharmony_ci break; 116862306a36Sopenharmony_ci case -EACCES: 116962306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY 117062306a36Sopenharmony_ci /* 117162306a36Sopenharmony_ci * perm errors, try again with backup flags if possible 117262306a36Sopenharmony_ci * 117362306a36Sopenharmony_ci * For SMB2 and later the backup intent flag 117462306a36Sopenharmony_ci * is already sent if needed on open and there 117562306a36Sopenharmony_ci * is no path based FindFirst operation to use 117662306a36Sopenharmony_ci * to retry with 117762306a36Sopenharmony_ci */ 117862306a36Sopenharmony_ci if (backup_cred(cifs_sb) && is_smb1_server(server)) { 117962306a36Sopenharmony_ci /* for easier reading */ 118062306a36Sopenharmony_ci FILE_ALL_INFO *fi; 118162306a36Sopenharmony_ci FILE_DIRECTORY_INFO *fdi; 118262306a36Sopenharmony_ci SEARCH_ID_FULL_DIR_INFO *si; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci rc = cifs_backup_query_path_info(xid, tcon, sb, 118562306a36Sopenharmony_ci full_path, 118662306a36Sopenharmony_ci &smb1_backup_rsp_buf, 118762306a36Sopenharmony_ci &fi); 118862306a36Sopenharmony_ci if (rc) 118962306a36Sopenharmony_ci goto out; 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci move_cifs_info_to_smb2(&data->fi, fi); 119262306a36Sopenharmony_ci fdi = (FILE_DIRECTORY_INFO *)fi; 119362306a36Sopenharmony_ci si = (SEARCH_ID_FULL_DIR_INFO *)fi; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci cifs_dir_info_to_fattr(fattr, fdi, cifs_sb); 119662306a36Sopenharmony_ci fattr->cf_uniqueid = le64_to_cpu(si->UniqueId); 119762306a36Sopenharmony_ci /* uniqueid set, skip get inum step */ 119862306a36Sopenharmony_ci goto handle_mnt_opt; 119962306a36Sopenharmony_ci } else { 120062306a36Sopenharmony_ci /* nothing we can do, bail out */ 120162306a36Sopenharmony_ci goto out; 120262306a36Sopenharmony_ci } 120362306a36Sopenharmony_ci#else 120462306a36Sopenharmony_ci goto out; 120562306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ 120662306a36Sopenharmony_ci break; 120762306a36Sopenharmony_ci default: 120862306a36Sopenharmony_ci cifs_dbg(FYI, "%s: unhandled err rc %d\n", __func__, rc); 120962306a36Sopenharmony_ci goto out; 121062306a36Sopenharmony_ci } 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci /* 121362306a36Sopenharmony_ci * 3. Get or update inode number (fattr->cf_uniqueid) 121462306a36Sopenharmony_ci */ 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci cifs_set_fattr_ino(xid, tcon, sb, inode, full_path, data, fattr); 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci /* 121962306a36Sopenharmony_ci * 4. Tweak fattr based on mount options 122062306a36Sopenharmony_ci */ 122162306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY 122262306a36Sopenharmony_cihandle_mnt_opt: 122362306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ 122462306a36Sopenharmony_ci /* query for SFU type info if supported and needed */ 122562306a36Sopenharmony_ci if ((fattr->cf_cifsattrs & ATTR_SYSTEM) && 122662306a36Sopenharmony_ci (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)) { 122762306a36Sopenharmony_ci tmprc = cifs_sfu_type(fattr, full_path, cifs_sb, xid); 122862306a36Sopenharmony_ci if (tmprc) 122962306a36Sopenharmony_ci cifs_dbg(FYI, "cifs_sfu_type failed: %d\n", tmprc); 123062306a36Sopenharmony_ci } 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci /* fill in 0777 bits from ACL */ 123362306a36Sopenharmony_ci if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) { 123462306a36Sopenharmony_ci rc = cifs_acl_to_fattr(cifs_sb, fattr, *inode, 123562306a36Sopenharmony_ci true, full_path, fid); 123662306a36Sopenharmony_ci if (rc == -EREMOTE) 123762306a36Sopenharmony_ci rc = 0; 123862306a36Sopenharmony_ci if (rc) { 123962306a36Sopenharmony_ci cifs_dbg(FYI, "%s: Get mode from SID failed. rc=%d\n", 124062306a36Sopenharmony_ci __func__, rc); 124162306a36Sopenharmony_ci goto out; 124262306a36Sopenharmony_ci } 124362306a36Sopenharmony_ci } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { 124462306a36Sopenharmony_ci rc = cifs_acl_to_fattr(cifs_sb, fattr, *inode, 124562306a36Sopenharmony_ci false, full_path, fid); 124662306a36Sopenharmony_ci if (rc == -EREMOTE) 124762306a36Sopenharmony_ci rc = 0; 124862306a36Sopenharmony_ci if (rc) { 124962306a36Sopenharmony_ci cifs_dbg(FYI, "%s: Getting ACL failed with error: %d\n", 125062306a36Sopenharmony_ci __func__, rc); 125162306a36Sopenharmony_ci goto out; 125262306a36Sopenharmony_ci } 125362306a36Sopenharmony_ci } 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci /* fill in remaining high mode bits e.g. SUID, VTX */ 125662306a36Sopenharmony_ci if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) 125762306a36Sopenharmony_ci cifs_sfu_mode(fattr, full_path, cifs_sb, xid); 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci /* check for Minshall+French symlinks */ 126062306a36Sopenharmony_ci if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) { 126162306a36Sopenharmony_ci tmprc = check_mf_symlink(xid, tcon, cifs_sb, fattr, full_path); 126262306a36Sopenharmony_ci cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc); 126362306a36Sopenharmony_ci } 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ciout: 126662306a36Sopenharmony_ci cifs_buf_release(smb1_backup_rsp_buf); 126762306a36Sopenharmony_ci cifs_put_tlink(tlink); 126862306a36Sopenharmony_ci cifs_free_open_info(&tmp_data); 126962306a36Sopenharmony_ci return rc; 127062306a36Sopenharmony_ci} 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ciint cifs_get_inode_info(struct inode **inode, 127362306a36Sopenharmony_ci const char *full_path, 127462306a36Sopenharmony_ci struct cifs_open_info_data *data, 127562306a36Sopenharmony_ci struct super_block *sb, int xid, 127662306a36Sopenharmony_ci const struct cifs_fid *fid) 127762306a36Sopenharmony_ci{ 127862306a36Sopenharmony_ci struct cifs_fattr fattr = {}; 127962306a36Sopenharmony_ci int rc; 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci if (is_inode_cache_good(*inode)) { 128262306a36Sopenharmony_ci cifs_dbg(FYI, "No need to revalidate cached inode sizes\n"); 128362306a36Sopenharmony_ci return 0; 128462306a36Sopenharmony_ci } 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci rc = cifs_get_fattr(data, sb, xid, fid, &fattr, inode, full_path); 128762306a36Sopenharmony_ci if (rc) 128862306a36Sopenharmony_ci goto out; 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci rc = update_inode_info(sb, &fattr, inode); 129162306a36Sopenharmony_ciout: 129262306a36Sopenharmony_ci kfree(fattr.cf_symlink_target); 129362306a36Sopenharmony_ci return rc; 129462306a36Sopenharmony_ci} 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_cistatic int smb311_posix_get_fattr(struct cifs_fattr *fattr, 129762306a36Sopenharmony_ci const char *full_path, 129862306a36Sopenharmony_ci struct super_block *sb, 129962306a36Sopenharmony_ci const unsigned int xid) 130062306a36Sopenharmony_ci{ 130162306a36Sopenharmony_ci struct cifs_open_info_data data = {}; 130262306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb = CIFS_SB(sb); 130362306a36Sopenharmony_ci struct cifs_tcon *tcon; 130462306a36Sopenharmony_ci struct tcon_link *tlink; 130562306a36Sopenharmony_ci struct cifs_sid owner, group; 130662306a36Sopenharmony_ci int tmprc; 130762306a36Sopenharmony_ci int rc; 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci tlink = cifs_sb_tlink(cifs_sb); 131062306a36Sopenharmony_ci if (IS_ERR(tlink)) 131162306a36Sopenharmony_ci return PTR_ERR(tlink); 131262306a36Sopenharmony_ci tcon = tlink_tcon(tlink); 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci /* 131562306a36Sopenharmony_ci * 1. Fetch file metadata 131662306a36Sopenharmony_ci */ 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci rc = smb311_posix_query_path_info(xid, tcon, cifs_sb, 131962306a36Sopenharmony_ci full_path, &data, 132062306a36Sopenharmony_ci &owner, &group); 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci /* 132362306a36Sopenharmony_ci * 2. Convert it to internal cifs metadata (fattr) 132462306a36Sopenharmony_ci */ 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci switch (rc) { 132762306a36Sopenharmony_ci case 0: 132862306a36Sopenharmony_ci smb311_posix_info_to_fattr(fattr, &data, &owner, &group, sb); 132962306a36Sopenharmony_ci break; 133062306a36Sopenharmony_ci case -EREMOTE: 133162306a36Sopenharmony_ci /* DFS link, no metadata available on this server */ 133262306a36Sopenharmony_ci cifs_create_junction_fattr(fattr, sb); 133362306a36Sopenharmony_ci rc = 0; 133462306a36Sopenharmony_ci break; 133562306a36Sopenharmony_ci case -EACCES: 133662306a36Sopenharmony_ci /* 133762306a36Sopenharmony_ci * For SMB2 and later the backup intent flag 133862306a36Sopenharmony_ci * is already sent if needed on open and there 133962306a36Sopenharmony_ci * is no path based FindFirst operation to use 134062306a36Sopenharmony_ci * to retry with so nothing we can do, bail out 134162306a36Sopenharmony_ci */ 134262306a36Sopenharmony_ci goto out; 134362306a36Sopenharmony_ci default: 134462306a36Sopenharmony_ci cifs_dbg(FYI, "%s: unhandled err rc %d\n", __func__, rc); 134562306a36Sopenharmony_ci goto out; 134662306a36Sopenharmony_ci } 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci /* 134962306a36Sopenharmony_ci * 3. Tweak fattr based on mount options 135062306a36Sopenharmony_ci */ 135162306a36Sopenharmony_ci /* check for Minshall+French symlinks */ 135262306a36Sopenharmony_ci if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) { 135362306a36Sopenharmony_ci tmprc = check_mf_symlink(xid, tcon, cifs_sb, fattr, full_path); 135462306a36Sopenharmony_ci cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc); 135562306a36Sopenharmony_ci } 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ciout: 135862306a36Sopenharmony_ci cifs_put_tlink(tlink); 135962306a36Sopenharmony_ci cifs_free_open_info(&data); 136062306a36Sopenharmony_ci return rc; 136162306a36Sopenharmony_ci} 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ciint smb311_posix_get_inode_info(struct inode **inode, const char *full_path, 136462306a36Sopenharmony_ci struct super_block *sb, const unsigned int xid) 136562306a36Sopenharmony_ci{ 136662306a36Sopenharmony_ci struct cifs_fattr fattr = {}; 136762306a36Sopenharmony_ci int rc; 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci if (is_inode_cache_good(*inode)) { 137062306a36Sopenharmony_ci cifs_dbg(FYI, "No need to revalidate cached inode sizes\n"); 137162306a36Sopenharmony_ci return 0; 137262306a36Sopenharmony_ci } 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci rc = smb311_posix_get_fattr(&fattr, full_path, sb, xid); 137562306a36Sopenharmony_ci if (rc) 137662306a36Sopenharmony_ci goto out; 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci rc = update_inode_info(sb, &fattr, inode); 137962306a36Sopenharmony_ciout: 138062306a36Sopenharmony_ci kfree(fattr.cf_symlink_target); 138162306a36Sopenharmony_ci return rc; 138262306a36Sopenharmony_ci} 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_cistatic const struct inode_operations cifs_ipc_inode_ops = { 138562306a36Sopenharmony_ci .lookup = cifs_lookup, 138662306a36Sopenharmony_ci}; 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_cistatic int 138962306a36Sopenharmony_cicifs_find_inode(struct inode *inode, void *opaque) 139062306a36Sopenharmony_ci{ 139162306a36Sopenharmony_ci struct cifs_fattr *fattr = opaque; 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci /* don't match inode with different uniqueid */ 139462306a36Sopenharmony_ci if (CIFS_I(inode)->uniqueid != fattr->cf_uniqueid) 139562306a36Sopenharmony_ci return 0; 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci /* use createtime like an i_generation field */ 139862306a36Sopenharmony_ci if (CIFS_I(inode)->createtime != fattr->cf_createtime) 139962306a36Sopenharmony_ci return 0; 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci /* don't match inode of different type */ 140262306a36Sopenharmony_ci if (inode_wrong_type(inode, fattr->cf_mode)) 140362306a36Sopenharmony_ci return 0; 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci /* if it's not a directory or has no dentries, then flag it */ 140662306a36Sopenharmony_ci if (S_ISDIR(inode->i_mode) && !hlist_empty(&inode->i_dentry)) 140762306a36Sopenharmony_ci fattr->cf_flags |= CIFS_FATTR_INO_COLLISION; 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci return 1; 141062306a36Sopenharmony_ci} 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_cistatic int 141362306a36Sopenharmony_cicifs_init_inode(struct inode *inode, void *opaque) 141462306a36Sopenharmony_ci{ 141562306a36Sopenharmony_ci struct cifs_fattr *fattr = opaque; 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci CIFS_I(inode)->uniqueid = fattr->cf_uniqueid; 141862306a36Sopenharmony_ci CIFS_I(inode)->createtime = fattr->cf_createtime; 141962306a36Sopenharmony_ci return 0; 142062306a36Sopenharmony_ci} 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci/* 142362306a36Sopenharmony_ci * walk dentry list for an inode and report whether it has aliases that 142462306a36Sopenharmony_ci * are hashed. We use this to determine if a directory inode can actually 142562306a36Sopenharmony_ci * be used. 142662306a36Sopenharmony_ci */ 142762306a36Sopenharmony_cistatic bool 142862306a36Sopenharmony_ciinode_has_hashed_dentries(struct inode *inode) 142962306a36Sopenharmony_ci{ 143062306a36Sopenharmony_ci struct dentry *dentry; 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci spin_lock(&inode->i_lock); 143362306a36Sopenharmony_ci hlist_for_each_entry(dentry, &inode->i_dentry, d_u.d_alias) { 143462306a36Sopenharmony_ci if (!d_unhashed(dentry) || IS_ROOT(dentry)) { 143562306a36Sopenharmony_ci spin_unlock(&inode->i_lock); 143662306a36Sopenharmony_ci return true; 143762306a36Sopenharmony_ci } 143862306a36Sopenharmony_ci } 143962306a36Sopenharmony_ci spin_unlock(&inode->i_lock); 144062306a36Sopenharmony_ci return false; 144162306a36Sopenharmony_ci} 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci/* Given fattrs, get a corresponding inode */ 144462306a36Sopenharmony_cistruct inode * 144562306a36Sopenharmony_cicifs_iget(struct super_block *sb, struct cifs_fattr *fattr) 144662306a36Sopenharmony_ci{ 144762306a36Sopenharmony_ci unsigned long hash; 144862306a36Sopenharmony_ci struct inode *inode; 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ciretry_iget5_locked: 145162306a36Sopenharmony_ci cifs_dbg(FYI, "looking for uniqueid=%llu\n", fattr->cf_uniqueid); 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci /* hash down to 32-bits on 32-bit arch */ 145462306a36Sopenharmony_ci hash = cifs_uniqueid_to_ino_t(fattr->cf_uniqueid); 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci inode = iget5_locked(sb, hash, cifs_find_inode, cifs_init_inode, fattr); 145762306a36Sopenharmony_ci if (inode) { 145862306a36Sopenharmony_ci /* was there a potentially problematic inode collision? */ 145962306a36Sopenharmony_ci if (fattr->cf_flags & CIFS_FATTR_INO_COLLISION) { 146062306a36Sopenharmony_ci fattr->cf_flags &= ~CIFS_FATTR_INO_COLLISION; 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci if (inode_has_hashed_dentries(inode)) { 146362306a36Sopenharmony_ci cifs_autodisable_serverino(CIFS_SB(sb)); 146462306a36Sopenharmony_ci iput(inode); 146562306a36Sopenharmony_ci fattr->cf_uniqueid = iunique(sb, ROOT_I); 146662306a36Sopenharmony_ci goto retry_iget5_locked; 146762306a36Sopenharmony_ci } 146862306a36Sopenharmony_ci } 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci /* can't fail - see cifs_find_inode() */ 147162306a36Sopenharmony_ci cifs_fattr_to_inode(inode, fattr, false); 147262306a36Sopenharmony_ci if (sb->s_flags & SB_NOATIME) 147362306a36Sopenharmony_ci inode->i_flags |= S_NOATIME | S_NOCMTIME; 147462306a36Sopenharmony_ci if (inode->i_state & I_NEW) { 147562306a36Sopenharmony_ci inode->i_ino = hash; 147662306a36Sopenharmony_ci cifs_fscache_get_inode_cookie(inode); 147762306a36Sopenharmony_ci unlock_new_inode(inode); 147862306a36Sopenharmony_ci } 147962306a36Sopenharmony_ci } 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci return inode; 148262306a36Sopenharmony_ci} 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci/* gets root inode */ 148562306a36Sopenharmony_cistruct inode *cifs_root_iget(struct super_block *sb) 148662306a36Sopenharmony_ci{ 148762306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb = CIFS_SB(sb); 148862306a36Sopenharmony_ci struct cifs_fattr fattr = {}; 148962306a36Sopenharmony_ci struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); 149062306a36Sopenharmony_ci struct inode *inode = NULL; 149162306a36Sopenharmony_ci unsigned int xid; 149262306a36Sopenharmony_ci char *path = NULL; 149362306a36Sopenharmony_ci int len; 149462306a36Sopenharmony_ci int rc; 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) 149762306a36Sopenharmony_ci && cifs_sb->prepath) { 149862306a36Sopenharmony_ci len = strlen(cifs_sb->prepath); 149962306a36Sopenharmony_ci path = kzalloc(len + 2 /* leading sep + null */, GFP_KERNEL); 150062306a36Sopenharmony_ci if (path == NULL) 150162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 150262306a36Sopenharmony_ci path[0] = '/'; 150362306a36Sopenharmony_ci memcpy(path+1, cifs_sb->prepath, len); 150462306a36Sopenharmony_ci } else { 150562306a36Sopenharmony_ci path = kstrdup("", GFP_KERNEL); 150662306a36Sopenharmony_ci if (path == NULL) 150762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 150862306a36Sopenharmony_ci } 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci xid = get_xid(); 151162306a36Sopenharmony_ci if (tcon->unix_ext) { 151262306a36Sopenharmony_ci rc = cifs_get_unix_fattr(path, sb, &fattr, &inode, xid); 151362306a36Sopenharmony_ci /* some servers mistakenly claim POSIX support */ 151462306a36Sopenharmony_ci if (rc != -EOPNOTSUPP) 151562306a36Sopenharmony_ci goto iget_root; 151662306a36Sopenharmony_ci cifs_dbg(VFS, "server does not support POSIX extensions\n"); 151762306a36Sopenharmony_ci tcon->unix_ext = false; 151862306a36Sopenharmony_ci } 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci convert_delimiter(path, CIFS_DIR_SEP(cifs_sb)); 152162306a36Sopenharmony_ci if (tcon->posix_extensions) 152262306a36Sopenharmony_ci rc = smb311_posix_get_fattr(&fattr, path, sb, xid); 152362306a36Sopenharmony_ci else 152462306a36Sopenharmony_ci rc = cifs_get_fattr(NULL, sb, xid, NULL, &fattr, &inode, path); 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_ciiget_root: 152762306a36Sopenharmony_ci if (!rc) { 152862306a36Sopenharmony_ci if (fattr.cf_flags & CIFS_FATTR_JUNCTION) { 152962306a36Sopenharmony_ci fattr.cf_flags &= ~CIFS_FATTR_JUNCTION; 153062306a36Sopenharmony_ci cifs_autodisable_serverino(cifs_sb); 153162306a36Sopenharmony_ci } 153262306a36Sopenharmony_ci inode = cifs_iget(sb, &fattr); 153362306a36Sopenharmony_ci } 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci if (!inode) { 153662306a36Sopenharmony_ci inode = ERR_PTR(rc); 153762306a36Sopenharmony_ci goto out; 153862306a36Sopenharmony_ci } 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_ci if (rc && tcon->pipe) { 154162306a36Sopenharmony_ci cifs_dbg(FYI, "ipc connection - fake read inode\n"); 154262306a36Sopenharmony_ci spin_lock(&inode->i_lock); 154362306a36Sopenharmony_ci inode->i_mode |= S_IFDIR; 154462306a36Sopenharmony_ci set_nlink(inode, 2); 154562306a36Sopenharmony_ci inode->i_op = &cifs_ipc_inode_ops; 154662306a36Sopenharmony_ci inode->i_fop = &simple_dir_operations; 154762306a36Sopenharmony_ci inode->i_uid = cifs_sb->ctx->linux_uid; 154862306a36Sopenharmony_ci inode->i_gid = cifs_sb->ctx->linux_gid; 154962306a36Sopenharmony_ci spin_unlock(&inode->i_lock); 155062306a36Sopenharmony_ci } else if (rc) { 155162306a36Sopenharmony_ci iget_failed(inode); 155262306a36Sopenharmony_ci inode = ERR_PTR(rc); 155362306a36Sopenharmony_ci } 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ciout: 155662306a36Sopenharmony_ci kfree(path); 155762306a36Sopenharmony_ci free_xid(xid); 155862306a36Sopenharmony_ci kfree(fattr.cf_symlink_target); 155962306a36Sopenharmony_ci return inode; 156062306a36Sopenharmony_ci} 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ciint 156362306a36Sopenharmony_cicifs_set_file_info(struct inode *inode, struct iattr *attrs, unsigned int xid, 156462306a36Sopenharmony_ci const char *full_path, __u32 dosattr) 156562306a36Sopenharmony_ci{ 156662306a36Sopenharmony_ci bool set_time = false; 156762306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); 156862306a36Sopenharmony_ci struct TCP_Server_Info *server; 156962306a36Sopenharmony_ci FILE_BASIC_INFO info_buf; 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci if (attrs == NULL) 157262306a36Sopenharmony_ci return -EINVAL; 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_ci server = cifs_sb_master_tcon(cifs_sb)->ses->server; 157562306a36Sopenharmony_ci if (!server->ops->set_file_info) 157662306a36Sopenharmony_ci return -ENOSYS; 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_ci info_buf.Pad = 0; 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ci if (attrs->ia_valid & ATTR_ATIME) { 158162306a36Sopenharmony_ci set_time = true; 158262306a36Sopenharmony_ci info_buf.LastAccessTime = 158362306a36Sopenharmony_ci cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_atime)); 158462306a36Sopenharmony_ci } else 158562306a36Sopenharmony_ci info_buf.LastAccessTime = 0; 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci if (attrs->ia_valid & ATTR_MTIME) { 158862306a36Sopenharmony_ci set_time = true; 158962306a36Sopenharmony_ci info_buf.LastWriteTime = 159062306a36Sopenharmony_ci cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_mtime)); 159162306a36Sopenharmony_ci } else 159262306a36Sopenharmony_ci info_buf.LastWriteTime = 0; 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci /* 159562306a36Sopenharmony_ci * Samba throws this field away, but windows may actually use it. 159662306a36Sopenharmony_ci * Do not set ctime unless other time stamps are changed explicitly 159762306a36Sopenharmony_ci * (i.e. by utimes()) since we would then have a mix of client and 159862306a36Sopenharmony_ci * server times. 159962306a36Sopenharmony_ci */ 160062306a36Sopenharmony_ci if (set_time && (attrs->ia_valid & ATTR_CTIME)) { 160162306a36Sopenharmony_ci cifs_dbg(FYI, "CIFS - CTIME changed\n"); 160262306a36Sopenharmony_ci info_buf.ChangeTime = 160362306a36Sopenharmony_ci cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_ctime)); 160462306a36Sopenharmony_ci } else 160562306a36Sopenharmony_ci info_buf.ChangeTime = 0; 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_ci info_buf.CreationTime = 0; /* don't change */ 160862306a36Sopenharmony_ci info_buf.Attributes = cpu_to_le32(dosattr); 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_ci return server->ops->set_file_info(inode, full_path, &info_buf, xid); 161162306a36Sopenharmony_ci} 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY 161462306a36Sopenharmony_ci/* 161562306a36Sopenharmony_ci * Open the given file (if it isn't already), set the DELETE_ON_CLOSE bit 161662306a36Sopenharmony_ci * and rename it to a random name that hopefully won't conflict with 161762306a36Sopenharmony_ci * anything else. 161862306a36Sopenharmony_ci */ 161962306a36Sopenharmony_ciint 162062306a36Sopenharmony_cicifs_rename_pending_delete(const char *full_path, struct dentry *dentry, 162162306a36Sopenharmony_ci const unsigned int xid) 162262306a36Sopenharmony_ci{ 162362306a36Sopenharmony_ci int oplock = 0; 162462306a36Sopenharmony_ci int rc; 162562306a36Sopenharmony_ci struct cifs_fid fid; 162662306a36Sopenharmony_ci struct cifs_open_parms oparms; 162762306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 162862306a36Sopenharmony_ci struct cifsInodeInfo *cifsInode = CIFS_I(inode); 162962306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); 163062306a36Sopenharmony_ci struct tcon_link *tlink; 163162306a36Sopenharmony_ci struct cifs_tcon *tcon; 163262306a36Sopenharmony_ci __u32 dosattr, origattr; 163362306a36Sopenharmony_ci FILE_BASIC_INFO *info_buf = NULL; 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ci tlink = cifs_sb_tlink(cifs_sb); 163662306a36Sopenharmony_ci if (IS_ERR(tlink)) 163762306a36Sopenharmony_ci return PTR_ERR(tlink); 163862306a36Sopenharmony_ci tcon = tlink_tcon(tlink); 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_ci /* 164162306a36Sopenharmony_ci * We cannot rename the file if the server doesn't support 164262306a36Sopenharmony_ci * CAP_INFOLEVEL_PASSTHRU 164362306a36Sopenharmony_ci */ 164462306a36Sopenharmony_ci if (!(tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)) { 164562306a36Sopenharmony_ci rc = -EBUSY; 164662306a36Sopenharmony_ci goto out; 164762306a36Sopenharmony_ci } 164862306a36Sopenharmony_ci 164962306a36Sopenharmony_ci oparms = (struct cifs_open_parms) { 165062306a36Sopenharmony_ci .tcon = tcon, 165162306a36Sopenharmony_ci .cifs_sb = cifs_sb, 165262306a36Sopenharmony_ci .desired_access = DELETE | FILE_WRITE_ATTRIBUTES, 165362306a36Sopenharmony_ci .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), 165462306a36Sopenharmony_ci .disposition = FILE_OPEN, 165562306a36Sopenharmony_ci .path = full_path, 165662306a36Sopenharmony_ci .fid = &fid, 165762306a36Sopenharmony_ci }; 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_ci rc = CIFS_open(xid, &oparms, &oplock, NULL); 166062306a36Sopenharmony_ci if (rc != 0) 166162306a36Sopenharmony_ci goto out; 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci origattr = cifsInode->cifsAttrs; 166462306a36Sopenharmony_ci if (origattr == 0) 166562306a36Sopenharmony_ci origattr |= ATTR_NORMAL; 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci dosattr = origattr & ~ATTR_READONLY; 166862306a36Sopenharmony_ci if (dosattr == 0) 166962306a36Sopenharmony_ci dosattr |= ATTR_NORMAL; 167062306a36Sopenharmony_ci dosattr |= ATTR_HIDDEN; 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci /* set ATTR_HIDDEN and clear ATTR_READONLY, but only if needed */ 167362306a36Sopenharmony_ci if (dosattr != origattr) { 167462306a36Sopenharmony_ci info_buf = kzalloc(sizeof(*info_buf), GFP_KERNEL); 167562306a36Sopenharmony_ci if (info_buf == NULL) { 167662306a36Sopenharmony_ci rc = -ENOMEM; 167762306a36Sopenharmony_ci goto out_close; 167862306a36Sopenharmony_ci } 167962306a36Sopenharmony_ci info_buf->Attributes = cpu_to_le32(dosattr); 168062306a36Sopenharmony_ci rc = CIFSSMBSetFileInfo(xid, tcon, info_buf, fid.netfid, 168162306a36Sopenharmony_ci current->tgid); 168262306a36Sopenharmony_ci /* although we would like to mark the file hidden 168362306a36Sopenharmony_ci if that fails we will still try to rename it */ 168462306a36Sopenharmony_ci if (!rc) 168562306a36Sopenharmony_ci cifsInode->cifsAttrs = dosattr; 168662306a36Sopenharmony_ci else 168762306a36Sopenharmony_ci dosattr = origattr; /* since not able to change them */ 168862306a36Sopenharmony_ci } 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_ci /* rename the file */ 169162306a36Sopenharmony_ci rc = CIFSSMBRenameOpenFile(xid, tcon, fid.netfid, NULL, 169262306a36Sopenharmony_ci cifs_sb->local_nls, 169362306a36Sopenharmony_ci cifs_remap(cifs_sb)); 169462306a36Sopenharmony_ci if (rc != 0) { 169562306a36Sopenharmony_ci rc = -EBUSY; 169662306a36Sopenharmony_ci goto undo_setattr; 169762306a36Sopenharmony_ci } 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci /* try to set DELETE_ON_CLOSE */ 170062306a36Sopenharmony_ci if (!test_bit(CIFS_INO_DELETE_PENDING, &cifsInode->flags)) { 170162306a36Sopenharmony_ci rc = CIFSSMBSetFileDisposition(xid, tcon, true, fid.netfid, 170262306a36Sopenharmony_ci current->tgid); 170362306a36Sopenharmony_ci /* 170462306a36Sopenharmony_ci * some samba versions return -ENOENT when we try to set the 170562306a36Sopenharmony_ci * file disposition here. Likely a samba bug, but work around 170662306a36Sopenharmony_ci * it for now. This means that some cifsXXX files may hang 170762306a36Sopenharmony_ci * around after they shouldn't. 170862306a36Sopenharmony_ci * 170962306a36Sopenharmony_ci * BB: remove this hack after more servers have the fix 171062306a36Sopenharmony_ci */ 171162306a36Sopenharmony_ci if (rc == -ENOENT) 171262306a36Sopenharmony_ci rc = 0; 171362306a36Sopenharmony_ci else if (rc != 0) { 171462306a36Sopenharmony_ci rc = -EBUSY; 171562306a36Sopenharmony_ci goto undo_rename; 171662306a36Sopenharmony_ci } 171762306a36Sopenharmony_ci set_bit(CIFS_INO_DELETE_PENDING, &cifsInode->flags); 171862306a36Sopenharmony_ci } 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_ciout_close: 172162306a36Sopenharmony_ci CIFSSMBClose(xid, tcon, fid.netfid); 172262306a36Sopenharmony_ciout: 172362306a36Sopenharmony_ci kfree(info_buf); 172462306a36Sopenharmony_ci cifs_put_tlink(tlink); 172562306a36Sopenharmony_ci return rc; 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_ci /* 172862306a36Sopenharmony_ci * reset everything back to the original state. Don't bother 172962306a36Sopenharmony_ci * dealing with errors here since we can't do anything about 173062306a36Sopenharmony_ci * them anyway. 173162306a36Sopenharmony_ci */ 173262306a36Sopenharmony_ciundo_rename: 173362306a36Sopenharmony_ci CIFSSMBRenameOpenFile(xid, tcon, fid.netfid, dentry->d_name.name, 173462306a36Sopenharmony_ci cifs_sb->local_nls, cifs_remap(cifs_sb)); 173562306a36Sopenharmony_ciundo_setattr: 173662306a36Sopenharmony_ci if (dosattr != origattr) { 173762306a36Sopenharmony_ci info_buf->Attributes = cpu_to_le32(origattr); 173862306a36Sopenharmony_ci if (!CIFSSMBSetFileInfo(xid, tcon, info_buf, fid.netfid, 173962306a36Sopenharmony_ci current->tgid)) 174062306a36Sopenharmony_ci cifsInode->cifsAttrs = origattr; 174162306a36Sopenharmony_ci } 174262306a36Sopenharmony_ci 174362306a36Sopenharmony_ci goto out_close; 174462306a36Sopenharmony_ci} 174562306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci/* copied from fs/nfs/dir.c with small changes */ 174862306a36Sopenharmony_cistatic void 174962306a36Sopenharmony_cicifs_drop_nlink(struct inode *inode) 175062306a36Sopenharmony_ci{ 175162306a36Sopenharmony_ci spin_lock(&inode->i_lock); 175262306a36Sopenharmony_ci if (inode->i_nlink > 0) 175362306a36Sopenharmony_ci drop_nlink(inode); 175462306a36Sopenharmony_ci spin_unlock(&inode->i_lock); 175562306a36Sopenharmony_ci} 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci/* 175862306a36Sopenharmony_ci * If d_inode(dentry) is null (usually meaning the cached dentry 175962306a36Sopenharmony_ci * is a negative dentry) then we would attempt a standard SMB delete, but 176062306a36Sopenharmony_ci * if that fails we can not attempt the fall back mechanisms on EACCES 176162306a36Sopenharmony_ci * but will return the EACCES to the caller. Note that the VFS does not call 176262306a36Sopenharmony_ci * unlink on negative dentries currently. 176362306a36Sopenharmony_ci */ 176462306a36Sopenharmony_ciint cifs_unlink(struct inode *dir, struct dentry *dentry) 176562306a36Sopenharmony_ci{ 176662306a36Sopenharmony_ci int rc = 0; 176762306a36Sopenharmony_ci unsigned int xid; 176862306a36Sopenharmony_ci const char *full_path; 176962306a36Sopenharmony_ci void *page; 177062306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 177162306a36Sopenharmony_ci struct cifsInodeInfo *cifs_inode; 177262306a36Sopenharmony_ci struct super_block *sb = dir->i_sb; 177362306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb = CIFS_SB(sb); 177462306a36Sopenharmony_ci struct tcon_link *tlink; 177562306a36Sopenharmony_ci struct cifs_tcon *tcon; 177662306a36Sopenharmony_ci struct TCP_Server_Info *server; 177762306a36Sopenharmony_ci struct iattr *attrs = NULL; 177862306a36Sopenharmony_ci __u32 dosattr = 0, origattr = 0; 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_ci cifs_dbg(FYI, "cifs_unlink, dir=0x%p, dentry=0x%p\n", dir, dentry); 178162306a36Sopenharmony_ci 178262306a36Sopenharmony_ci if (unlikely(cifs_forced_shutdown(cifs_sb))) 178362306a36Sopenharmony_ci return -EIO; 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci tlink = cifs_sb_tlink(cifs_sb); 178662306a36Sopenharmony_ci if (IS_ERR(tlink)) 178762306a36Sopenharmony_ci return PTR_ERR(tlink); 178862306a36Sopenharmony_ci tcon = tlink_tcon(tlink); 178962306a36Sopenharmony_ci server = tcon->ses->server; 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_ci xid = get_xid(); 179262306a36Sopenharmony_ci page = alloc_dentry_path(); 179362306a36Sopenharmony_ci 179462306a36Sopenharmony_ci if (tcon->nodelete) { 179562306a36Sopenharmony_ci rc = -EACCES; 179662306a36Sopenharmony_ci goto unlink_out; 179762306a36Sopenharmony_ci } 179862306a36Sopenharmony_ci 179962306a36Sopenharmony_ci /* Unlink can be called from rename so we can not take the 180062306a36Sopenharmony_ci * sb->s_vfs_rename_mutex here */ 180162306a36Sopenharmony_ci full_path = build_path_from_dentry(dentry, page); 180262306a36Sopenharmony_ci if (IS_ERR(full_path)) { 180362306a36Sopenharmony_ci rc = PTR_ERR(full_path); 180462306a36Sopenharmony_ci goto unlink_out; 180562306a36Sopenharmony_ci } 180662306a36Sopenharmony_ci 180762306a36Sopenharmony_ci cifs_close_deferred_file_under_dentry(tcon, full_path); 180862306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY 180962306a36Sopenharmony_ci if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & 181062306a36Sopenharmony_ci le64_to_cpu(tcon->fsUnixInfo.Capability))) { 181162306a36Sopenharmony_ci rc = CIFSPOSIXDelFile(xid, tcon, full_path, 181262306a36Sopenharmony_ci SMB_POSIX_UNLINK_FILE_TARGET, cifs_sb->local_nls, 181362306a36Sopenharmony_ci cifs_remap(cifs_sb)); 181462306a36Sopenharmony_ci cifs_dbg(FYI, "posix del rc %d\n", rc); 181562306a36Sopenharmony_ci if ((rc == 0) || (rc == -ENOENT)) 181662306a36Sopenharmony_ci goto psx_del_no_retry; 181762306a36Sopenharmony_ci } 181862306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ 181962306a36Sopenharmony_ci 182062306a36Sopenharmony_ciretry_std_delete: 182162306a36Sopenharmony_ci if (!server->ops->unlink) { 182262306a36Sopenharmony_ci rc = -ENOSYS; 182362306a36Sopenharmony_ci goto psx_del_no_retry; 182462306a36Sopenharmony_ci } 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci rc = server->ops->unlink(xid, tcon, full_path, cifs_sb); 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_cipsx_del_no_retry: 182962306a36Sopenharmony_ci if (!rc) { 183062306a36Sopenharmony_ci if (inode) 183162306a36Sopenharmony_ci cifs_drop_nlink(inode); 183262306a36Sopenharmony_ci } else if (rc == -ENOENT) { 183362306a36Sopenharmony_ci d_drop(dentry); 183462306a36Sopenharmony_ci } else if (rc == -EBUSY) { 183562306a36Sopenharmony_ci if (server->ops->rename_pending_delete) { 183662306a36Sopenharmony_ci rc = server->ops->rename_pending_delete(full_path, 183762306a36Sopenharmony_ci dentry, xid); 183862306a36Sopenharmony_ci if (rc == 0) 183962306a36Sopenharmony_ci cifs_drop_nlink(inode); 184062306a36Sopenharmony_ci } 184162306a36Sopenharmony_ci } else if ((rc == -EACCES) && (dosattr == 0) && inode) { 184262306a36Sopenharmony_ci attrs = kzalloc(sizeof(*attrs), GFP_KERNEL); 184362306a36Sopenharmony_ci if (attrs == NULL) { 184462306a36Sopenharmony_ci rc = -ENOMEM; 184562306a36Sopenharmony_ci goto out_reval; 184662306a36Sopenharmony_ci } 184762306a36Sopenharmony_ci 184862306a36Sopenharmony_ci /* try to reset dos attributes */ 184962306a36Sopenharmony_ci cifs_inode = CIFS_I(inode); 185062306a36Sopenharmony_ci origattr = cifs_inode->cifsAttrs; 185162306a36Sopenharmony_ci if (origattr == 0) 185262306a36Sopenharmony_ci origattr |= ATTR_NORMAL; 185362306a36Sopenharmony_ci dosattr = origattr & ~ATTR_READONLY; 185462306a36Sopenharmony_ci if (dosattr == 0) 185562306a36Sopenharmony_ci dosattr |= ATTR_NORMAL; 185662306a36Sopenharmony_ci dosattr |= ATTR_HIDDEN; 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci rc = cifs_set_file_info(inode, attrs, xid, full_path, dosattr); 185962306a36Sopenharmony_ci if (rc != 0) 186062306a36Sopenharmony_ci goto out_reval; 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci goto retry_std_delete; 186362306a36Sopenharmony_ci } 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ci /* undo the setattr if we errored out and it's needed */ 186662306a36Sopenharmony_ci if (rc != 0 && dosattr != 0) 186762306a36Sopenharmony_ci cifs_set_file_info(inode, attrs, xid, full_path, origattr); 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_ciout_reval: 187062306a36Sopenharmony_ci if (inode) { 187162306a36Sopenharmony_ci cifs_inode = CIFS_I(inode); 187262306a36Sopenharmony_ci cifs_inode->time = 0; /* will force revalidate to get info 187362306a36Sopenharmony_ci when needed */ 187462306a36Sopenharmony_ci inode_set_ctime_current(inode); 187562306a36Sopenharmony_ci } 187662306a36Sopenharmony_ci inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir)); 187762306a36Sopenharmony_ci cifs_inode = CIFS_I(dir); 187862306a36Sopenharmony_ci CIFS_I(dir)->time = 0; /* force revalidate of dir as well */ 187962306a36Sopenharmony_ciunlink_out: 188062306a36Sopenharmony_ci free_dentry_path(page); 188162306a36Sopenharmony_ci kfree(attrs); 188262306a36Sopenharmony_ci free_xid(xid); 188362306a36Sopenharmony_ci cifs_put_tlink(tlink); 188462306a36Sopenharmony_ci return rc; 188562306a36Sopenharmony_ci} 188662306a36Sopenharmony_ci 188762306a36Sopenharmony_cistatic int 188862306a36Sopenharmony_cicifs_mkdir_qinfo(struct inode *parent, struct dentry *dentry, umode_t mode, 188962306a36Sopenharmony_ci const char *full_path, struct cifs_sb_info *cifs_sb, 189062306a36Sopenharmony_ci struct cifs_tcon *tcon, const unsigned int xid) 189162306a36Sopenharmony_ci{ 189262306a36Sopenharmony_ci int rc = 0; 189362306a36Sopenharmony_ci struct inode *inode = NULL; 189462306a36Sopenharmony_ci 189562306a36Sopenharmony_ci if (tcon->posix_extensions) 189662306a36Sopenharmony_ci rc = smb311_posix_get_inode_info(&inode, full_path, parent->i_sb, xid); 189762306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY 189862306a36Sopenharmony_ci else if (tcon->unix_ext) 189962306a36Sopenharmony_ci rc = cifs_get_inode_info_unix(&inode, full_path, parent->i_sb, 190062306a36Sopenharmony_ci xid); 190162306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ 190262306a36Sopenharmony_ci else 190362306a36Sopenharmony_ci rc = cifs_get_inode_info(&inode, full_path, NULL, parent->i_sb, 190462306a36Sopenharmony_ci xid, NULL); 190562306a36Sopenharmony_ci 190662306a36Sopenharmony_ci if (rc) 190762306a36Sopenharmony_ci return rc; 190862306a36Sopenharmony_ci 190962306a36Sopenharmony_ci if (!S_ISDIR(inode->i_mode)) { 191062306a36Sopenharmony_ci /* 191162306a36Sopenharmony_ci * mkdir succeeded, but another client has managed to remove the 191262306a36Sopenharmony_ci * sucker and replace it with non-directory. Return success, 191362306a36Sopenharmony_ci * but don't leave the child in dcache. 191462306a36Sopenharmony_ci */ 191562306a36Sopenharmony_ci iput(inode); 191662306a36Sopenharmony_ci d_drop(dentry); 191762306a36Sopenharmony_ci return 0; 191862306a36Sopenharmony_ci } 191962306a36Sopenharmony_ci /* 192062306a36Sopenharmony_ci * setting nlink not necessary except in cases where we failed to get it 192162306a36Sopenharmony_ci * from the server or was set bogus. Also, since this is a brand new 192262306a36Sopenharmony_ci * inode, no need to grab the i_lock before setting the i_nlink. 192362306a36Sopenharmony_ci */ 192462306a36Sopenharmony_ci if (inode->i_nlink < 2) 192562306a36Sopenharmony_ci set_nlink(inode, 2); 192662306a36Sopenharmony_ci mode &= ~current_umask(); 192762306a36Sopenharmony_ci /* must turn on setgid bit if parent dir has it */ 192862306a36Sopenharmony_ci if (parent->i_mode & S_ISGID) 192962306a36Sopenharmony_ci mode |= S_ISGID; 193062306a36Sopenharmony_ci 193162306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY 193262306a36Sopenharmony_ci if (tcon->unix_ext) { 193362306a36Sopenharmony_ci struct cifs_unix_set_info_args args = { 193462306a36Sopenharmony_ci .mode = mode, 193562306a36Sopenharmony_ci .ctime = NO_CHANGE_64, 193662306a36Sopenharmony_ci .atime = NO_CHANGE_64, 193762306a36Sopenharmony_ci .mtime = NO_CHANGE_64, 193862306a36Sopenharmony_ci .device = 0, 193962306a36Sopenharmony_ci }; 194062306a36Sopenharmony_ci if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { 194162306a36Sopenharmony_ci args.uid = current_fsuid(); 194262306a36Sopenharmony_ci if (parent->i_mode & S_ISGID) 194362306a36Sopenharmony_ci args.gid = parent->i_gid; 194462306a36Sopenharmony_ci else 194562306a36Sopenharmony_ci args.gid = current_fsgid(); 194662306a36Sopenharmony_ci } else { 194762306a36Sopenharmony_ci args.uid = INVALID_UID; /* no change */ 194862306a36Sopenharmony_ci args.gid = INVALID_GID; /* no change */ 194962306a36Sopenharmony_ci } 195062306a36Sopenharmony_ci CIFSSMBUnixSetPathInfo(xid, tcon, full_path, &args, 195162306a36Sopenharmony_ci cifs_sb->local_nls, 195262306a36Sopenharmony_ci cifs_remap(cifs_sb)); 195362306a36Sopenharmony_ci } else { 195462306a36Sopenharmony_ci#else 195562306a36Sopenharmony_ci { 195662306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ 195762306a36Sopenharmony_ci struct TCP_Server_Info *server = tcon->ses->server; 195862306a36Sopenharmony_ci if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) && 195962306a36Sopenharmony_ci (mode & S_IWUGO) == 0 && server->ops->mkdir_setinfo) 196062306a36Sopenharmony_ci server->ops->mkdir_setinfo(inode, full_path, cifs_sb, 196162306a36Sopenharmony_ci tcon, xid); 196262306a36Sopenharmony_ci if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) 196362306a36Sopenharmony_ci inode->i_mode = (mode | S_IFDIR); 196462306a36Sopenharmony_ci 196562306a36Sopenharmony_ci if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { 196662306a36Sopenharmony_ci inode->i_uid = current_fsuid(); 196762306a36Sopenharmony_ci if (inode->i_mode & S_ISGID) 196862306a36Sopenharmony_ci inode->i_gid = parent->i_gid; 196962306a36Sopenharmony_ci else 197062306a36Sopenharmony_ci inode->i_gid = current_fsgid(); 197162306a36Sopenharmony_ci } 197262306a36Sopenharmony_ci } 197362306a36Sopenharmony_ci d_instantiate(dentry, inode); 197462306a36Sopenharmony_ci return 0; 197562306a36Sopenharmony_ci} 197662306a36Sopenharmony_ci 197762306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY 197862306a36Sopenharmony_cistatic int 197962306a36Sopenharmony_cicifs_posix_mkdir(struct inode *inode, struct dentry *dentry, umode_t mode, 198062306a36Sopenharmony_ci const char *full_path, struct cifs_sb_info *cifs_sb, 198162306a36Sopenharmony_ci struct cifs_tcon *tcon, const unsigned int xid) 198262306a36Sopenharmony_ci{ 198362306a36Sopenharmony_ci int rc = 0; 198462306a36Sopenharmony_ci u32 oplock = 0; 198562306a36Sopenharmony_ci FILE_UNIX_BASIC_INFO *info = NULL; 198662306a36Sopenharmony_ci struct inode *newinode = NULL; 198762306a36Sopenharmony_ci struct cifs_fattr fattr; 198862306a36Sopenharmony_ci 198962306a36Sopenharmony_ci info = kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL); 199062306a36Sopenharmony_ci if (info == NULL) { 199162306a36Sopenharmony_ci rc = -ENOMEM; 199262306a36Sopenharmony_ci goto posix_mkdir_out; 199362306a36Sopenharmony_ci } 199462306a36Sopenharmony_ci 199562306a36Sopenharmony_ci mode &= ~current_umask(); 199662306a36Sopenharmony_ci rc = CIFSPOSIXCreate(xid, tcon, SMB_O_DIRECTORY | SMB_O_CREAT, mode, 199762306a36Sopenharmony_ci NULL /* netfid */, info, &oplock, full_path, 199862306a36Sopenharmony_ci cifs_sb->local_nls, cifs_remap(cifs_sb)); 199962306a36Sopenharmony_ci if (rc == -EOPNOTSUPP) 200062306a36Sopenharmony_ci goto posix_mkdir_out; 200162306a36Sopenharmony_ci else if (rc) { 200262306a36Sopenharmony_ci cifs_dbg(FYI, "posix mkdir returned 0x%x\n", rc); 200362306a36Sopenharmony_ci d_drop(dentry); 200462306a36Sopenharmony_ci goto posix_mkdir_out; 200562306a36Sopenharmony_ci } 200662306a36Sopenharmony_ci 200762306a36Sopenharmony_ci if (info->Type == cpu_to_le32(-1)) 200862306a36Sopenharmony_ci /* no return info, go query for it */ 200962306a36Sopenharmony_ci goto posix_mkdir_get_info; 201062306a36Sopenharmony_ci /* 201162306a36Sopenharmony_ci * BB check (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID ) to see if 201262306a36Sopenharmony_ci * need to set uid/gid. 201362306a36Sopenharmony_ci */ 201462306a36Sopenharmony_ci 201562306a36Sopenharmony_ci cifs_unix_basic_to_fattr(&fattr, info, cifs_sb); 201662306a36Sopenharmony_ci cifs_fill_uniqueid(inode->i_sb, &fattr); 201762306a36Sopenharmony_ci newinode = cifs_iget(inode->i_sb, &fattr); 201862306a36Sopenharmony_ci if (!newinode) 201962306a36Sopenharmony_ci goto posix_mkdir_get_info; 202062306a36Sopenharmony_ci 202162306a36Sopenharmony_ci d_instantiate(dentry, newinode); 202262306a36Sopenharmony_ci 202362306a36Sopenharmony_ci#ifdef CONFIG_CIFS_DEBUG2 202462306a36Sopenharmony_ci cifs_dbg(FYI, "instantiated dentry %p %pd to inode %p\n", 202562306a36Sopenharmony_ci dentry, dentry, newinode); 202662306a36Sopenharmony_ci 202762306a36Sopenharmony_ci if (newinode->i_nlink != 2) 202862306a36Sopenharmony_ci cifs_dbg(FYI, "unexpected number of links %d\n", 202962306a36Sopenharmony_ci newinode->i_nlink); 203062306a36Sopenharmony_ci#endif 203162306a36Sopenharmony_ci 203262306a36Sopenharmony_ciposix_mkdir_out: 203362306a36Sopenharmony_ci kfree(info); 203462306a36Sopenharmony_ci return rc; 203562306a36Sopenharmony_ciposix_mkdir_get_info: 203662306a36Sopenharmony_ci rc = cifs_mkdir_qinfo(inode, dentry, mode, full_path, cifs_sb, tcon, 203762306a36Sopenharmony_ci xid); 203862306a36Sopenharmony_ci goto posix_mkdir_out; 203962306a36Sopenharmony_ci} 204062306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ 204162306a36Sopenharmony_ci 204262306a36Sopenharmony_ciint cifs_mkdir(struct mnt_idmap *idmap, struct inode *inode, 204362306a36Sopenharmony_ci struct dentry *direntry, umode_t mode) 204462306a36Sopenharmony_ci{ 204562306a36Sopenharmony_ci int rc = 0; 204662306a36Sopenharmony_ci unsigned int xid; 204762306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb; 204862306a36Sopenharmony_ci struct tcon_link *tlink; 204962306a36Sopenharmony_ci struct cifs_tcon *tcon; 205062306a36Sopenharmony_ci struct TCP_Server_Info *server; 205162306a36Sopenharmony_ci const char *full_path; 205262306a36Sopenharmony_ci void *page; 205362306a36Sopenharmony_ci 205462306a36Sopenharmony_ci cifs_dbg(FYI, "In cifs_mkdir, mode = %04ho inode = 0x%p\n", 205562306a36Sopenharmony_ci mode, inode); 205662306a36Sopenharmony_ci 205762306a36Sopenharmony_ci cifs_sb = CIFS_SB(inode->i_sb); 205862306a36Sopenharmony_ci if (unlikely(cifs_forced_shutdown(cifs_sb))) 205962306a36Sopenharmony_ci return -EIO; 206062306a36Sopenharmony_ci tlink = cifs_sb_tlink(cifs_sb); 206162306a36Sopenharmony_ci if (IS_ERR(tlink)) 206262306a36Sopenharmony_ci return PTR_ERR(tlink); 206362306a36Sopenharmony_ci tcon = tlink_tcon(tlink); 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_ci xid = get_xid(); 206662306a36Sopenharmony_ci 206762306a36Sopenharmony_ci page = alloc_dentry_path(); 206862306a36Sopenharmony_ci full_path = build_path_from_dentry(direntry, page); 206962306a36Sopenharmony_ci if (IS_ERR(full_path)) { 207062306a36Sopenharmony_ci rc = PTR_ERR(full_path); 207162306a36Sopenharmony_ci goto mkdir_out; 207262306a36Sopenharmony_ci } 207362306a36Sopenharmony_ci 207462306a36Sopenharmony_ci server = tcon->ses->server; 207562306a36Sopenharmony_ci 207662306a36Sopenharmony_ci if ((server->ops->posix_mkdir) && (tcon->posix_extensions)) { 207762306a36Sopenharmony_ci rc = server->ops->posix_mkdir(xid, inode, mode, tcon, full_path, 207862306a36Sopenharmony_ci cifs_sb); 207962306a36Sopenharmony_ci d_drop(direntry); /* for time being always refresh inode info */ 208062306a36Sopenharmony_ci goto mkdir_out; 208162306a36Sopenharmony_ci } 208262306a36Sopenharmony_ci 208362306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY 208462306a36Sopenharmony_ci if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & 208562306a36Sopenharmony_ci le64_to_cpu(tcon->fsUnixInfo.Capability))) { 208662306a36Sopenharmony_ci rc = cifs_posix_mkdir(inode, direntry, mode, full_path, cifs_sb, 208762306a36Sopenharmony_ci tcon, xid); 208862306a36Sopenharmony_ci if (rc != -EOPNOTSUPP) 208962306a36Sopenharmony_ci goto mkdir_out; 209062306a36Sopenharmony_ci } 209162306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ 209262306a36Sopenharmony_ci 209362306a36Sopenharmony_ci if (!server->ops->mkdir) { 209462306a36Sopenharmony_ci rc = -ENOSYS; 209562306a36Sopenharmony_ci goto mkdir_out; 209662306a36Sopenharmony_ci } 209762306a36Sopenharmony_ci 209862306a36Sopenharmony_ci /* BB add setting the equivalent of mode via CreateX w/ACLs */ 209962306a36Sopenharmony_ci rc = server->ops->mkdir(xid, inode, mode, tcon, full_path, cifs_sb); 210062306a36Sopenharmony_ci if (rc) { 210162306a36Sopenharmony_ci cifs_dbg(FYI, "cifs_mkdir returned 0x%x\n", rc); 210262306a36Sopenharmony_ci d_drop(direntry); 210362306a36Sopenharmony_ci goto mkdir_out; 210462306a36Sopenharmony_ci } 210562306a36Sopenharmony_ci 210662306a36Sopenharmony_ci /* TODO: skip this for smb2/smb3 */ 210762306a36Sopenharmony_ci rc = cifs_mkdir_qinfo(inode, direntry, mode, full_path, cifs_sb, tcon, 210862306a36Sopenharmony_ci xid); 210962306a36Sopenharmony_cimkdir_out: 211062306a36Sopenharmony_ci /* 211162306a36Sopenharmony_ci * Force revalidate to get parent dir info when needed since cached 211262306a36Sopenharmony_ci * attributes are invalid now. 211362306a36Sopenharmony_ci */ 211462306a36Sopenharmony_ci CIFS_I(inode)->time = 0; 211562306a36Sopenharmony_ci free_dentry_path(page); 211662306a36Sopenharmony_ci free_xid(xid); 211762306a36Sopenharmony_ci cifs_put_tlink(tlink); 211862306a36Sopenharmony_ci return rc; 211962306a36Sopenharmony_ci} 212062306a36Sopenharmony_ci 212162306a36Sopenharmony_ciint cifs_rmdir(struct inode *inode, struct dentry *direntry) 212262306a36Sopenharmony_ci{ 212362306a36Sopenharmony_ci int rc = 0; 212462306a36Sopenharmony_ci unsigned int xid; 212562306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb; 212662306a36Sopenharmony_ci struct tcon_link *tlink; 212762306a36Sopenharmony_ci struct cifs_tcon *tcon; 212862306a36Sopenharmony_ci struct TCP_Server_Info *server; 212962306a36Sopenharmony_ci const char *full_path; 213062306a36Sopenharmony_ci void *page = alloc_dentry_path(); 213162306a36Sopenharmony_ci struct cifsInodeInfo *cifsInode; 213262306a36Sopenharmony_ci 213362306a36Sopenharmony_ci cifs_dbg(FYI, "cifs_rmdir, inode = 0x%p\n", inode); 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_ci xid = get_xid(); 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_ci full_path = build_path_from_dentry(direntry, page); 213862306a36Sopenharmony_ci if (IS_ERR(full_path)) { 213962306a36Sopenharmony_ci rc = PTR_ERR(full_path); 214062306a36Sopenharmony_ci goto rmdir_exit; 214162306a36Sopenharmony_ci } 214262306a36Sopenharmony_ci 214362306a36Sopenharmony_ci cifs_sb = CIFS_SB(inode->i_sb); 214462306a36Sopenharmony_ci if (unlikely(cifs_forced_shutdown(cifs_sb))) { 214562306a36Sopenharmony_ci rc = -EIO; 214662306a36Sopenharmony_ci goto rmdir_exit; 214762306a36Sopenharmony_ci } 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci tlink = cifs_sb_tlink(cifs_sb); 215062306a36Sopenharmony_ci if (IS_ERR(tlink)) { 215162306a36Sopenharmony_ci rc = PTR_ERR(tlink); 215262306a36Sopenharmony_ci goto rmdir_exit; 215362306a36Sopenharmony_ci } 215462306a36Sopenharmony_ci tcon = tlink_tcon(tlink); 215562306a36Sopenharmony_ci server = tcon->ses->server; 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_ci if (!server->ops->rmdir) { 215862306a36Sopenharmony_ci rc = -ENOSYS; 215962306a36Sopenharmony_ci cifs_put_tlink(tlink); 216062306a36Sopenharmony_ci goto rmdir_exit; 216162306a36Sopenharmony_ci } 216262306a36Sopenharmony_ci 216362306a36Sopenharmony_ci if (tcon->nodelete) { 216462306a36Sopenharmony_ci rc = -EACCES; 216562306a36Sopenharmony_ci cifs_put_tlink(tlink); 216662306a36Sopenharmony_ci goto rmdir_exit; 216762306a36Sopenharmony_ci } 216862306a36Sopenharmony_ci 216962306a36Sopenharmony_ci rc = server->ops->rmdir(xid, tcon, full_path, cifs_sb); 217062306a36Sopenharmony_ci cifs_put_tlink(tlink); 217162306a36Sopenharmony_ci 217262306a36Sopenharmony_ci if (!rc) { 217362306a36Sopenharmony_ci spin_lock(&d_inode(direntry)->i_lock); 217462306a36Sopenharmony_ci i_size_write(d_inode(direntry), 0); 217562306a36Sopenharmony_ci clear_nlink(d_inode(direntry)); 217662306a36Sopenharmony_ci spin_unlock(&d_inode(direntry)->i_lock); 217762306a36Sopenharmony_ci } 217862306a36Sopenharmony_ci 217962306a36Sopenharmony_ci cifsInode = CIFS_I(d_inode(direntry)); 218062306a36Sopenharmony_ci /* force revalidate to go get info when needed */ 218162306a36Sopenharmony_ci cifsInode->time = 0; 218262306a36Sopenharmony_ci 218362306a36Sopenharmony_ci cifsInode = CIFS_I(inode); 218462306a36Sopenharmony_ci /* 218562306a36Sopenharmony_ci * Force revalidate to get parent dir info when needed since cached 218662306a36Sopenharmony_ci * attributes are invalid now. 218762306a36Sopenharmony_ci */ 218862306a36Sopenharmony_ci cifsInode->time = 0; 218962306a36Sopenharmony_ci 219062306a36Sopenharmony_ci inode_set_ctime_current(d_inode(direntry)); 219162306a36Sopenharmony_ci inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); 219262306a36Sopenharmony_ci 219362306a36Sopenharmony_cirmdir_exit: 219462306a36Sopenharmony_ci free_dentry_path(page); 219562306a36Sopenharmony_ci free_xid(xid); 219662306a36Sopenharmony_ci return rc; 219762306a36Sopenharmony_ci} 219862306a36Sopenharmony_ci 219962306a36Sopenharmony_cistatic int 220062306a36Sopenharmony_cicifs_do_rename(const unsigned int xid, struct dentry *from_dentry, 220162306a36Sopenharmony_ci const char *from_path, struct dentry *to_dentry, 220262306a36Sopenharmony_ci const char *to_path) 220362306a36Sopenharmony_ci{ 220462306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb = CIFS_SB(from_dentry->d_sb); 220562306a36Sopenharmony_ci struct tcon_link *tlink; 220662306a36Sopenharmony_ci struct cifs_tcon *tcon; 220762306a36Sopenharmony_ci struct TCP_Server_Info *server; 220862306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY 220962306a36Sopenharmony_ci struct cifs_fid fid; 221062306a36Sopenharmony_ci struct cifs_open_parms oparms; 221162306a36Sopenharmony_ci int oplock; 221262306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ 221362306a36Sopenharmony_ci int rc; 221462306a36Sopenharmony_ci 221562306a36Sopenharmony_ci tlink = cifs_sb_tlink(cifs_sb); 221662306a36Sopenharmony_ci if (IS_ERR(tlink)) 221762306a36Sopenharmony_ci return PTR_ERR(tlink); 221862306a36Sopenharmony_ci tcon = tlink_tcon(tlink); 221962306a36Sopenharmony_ci server = tcon->ses->server; 222062306a36Sopenharmony_ci 222162306a36Sopenharmony_ci if (!server->ops->rename) 222262306a36Sopenharmony_ci return -ENOSYS; 222362306a36Sopenharmony_ci 222462306a36Sopenharmony_ci /* try path-based rename first */ 222562306a36Sopenharmony_ci rc = server->ops->rename(xid, tcon, from_dentry, 222662306a36Sopenharmony_ci from_path, to_path, cifs_sb); 222762306a36Sopenharmony_ci 222862306a36Sopenharmony_ci /* 222962306a36Sopenharmony_ci * Don't bother with rename by filehandle unless file is busy and 223062306a36Sopenharmony_ci * source. Note that cross directory moves do not work with 223162306a36Sopenharmony_ci * rename by filehandle to various Windows servers. 223262306a36Sopenharmony_ci */ 223362306a36Sopenharmony_ci if (rc == 0 || rc != -EBUSY) 223462306a36Sopenharmony_ci goto do_rename_exit; 223562306a36Sopenharmony_ci 223662306a36Sopenharmony_ci /* Don't fall back to using SMB on SMB 2+ mount */ 223762306a36Sopenharmony_ci if (server->vals->protocol_id != 0) 223862306a36Sopenharmony_ci goto do_rename_exit; 223962306a36Sopenharmony_ci 224062306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY 224162306a36Sopenharmony_ci /* open-file renames don't work across directories */ 224262306a36Sopenharmony_ci if (to_dentry->d_parent != from_dentry->d_parent) 224362306a36Sopenharmony_ci goto do_rename_exit; 224462306a36Sopenharmony_ci 224562306a36Sopenharmony_ci oparms = (struct cifs_open_parms) { 224662306a36Sopenharmony_ci .tcon = tcon, 224762306a36Sopenharmony_ci .cifs_sb = cifs_sb, 224862306a36Sopenharmony_ci /* open the file to be renamed -- we need DELETE perms */ 224962306a36Sopenharmony_ci .desired_access = DELETE, 225062306a36Sopenharmony_ci .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR), 225162306a36Sopenharmony_ci .disposition = FILE_OPEN, 225262306a36Sopenharmony_ci .path = from_path, 225362306a36Sopenharmony_ci .fid = &fid, 225462306a36Sopenharmony_ci }; 225562306a36Sopenharmony_ci 225662306a36Sopenharmony_ci rc = CIFS_open(xid, &oparms, &oplock, NULL); 225762306a36Sopenharmony_ci if (rc == 0) { 225862306a36Sopenharmony_ci rc = CIFSSMBRenameOpenFile(xid, tcon, fid.netfid, 225962306a36Sopenharmony_ci (const char *) to_dentry->d_name.name, 226062306a36Sopenharmony_ci cifs_sb->local_nls, cifs_remap(cifs_sb)); 226162306a36Sopenharmony_ci CIFSSMBClose(xid, tcon, fid.netfid); 226262306a36Sopenharmony_ci } 226362306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ 226462306a36Sopenharmony_cido_rename_exit: 226562306a36Sopenharmony_ci if (rc == 0) 226662306a36Sopenharmony_ci d_move(from_dentry, to_dentry); 226762306a36Sopenharmony_ci cifs_put_tlink(tlink); 226862306a36Sopenharmony_ci return rc; 226962306a36Sopenharmony_ci} 227062306a36Sopenharmony_ci 227162306a36Sopenharmony_ciint 227262306a36Sopenharmony_cicifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir, 227362306a36Sopenharmony_ci struct dentry *source_dentry, struct inode *target_dir, 227462306a36Sopenharmony_ci struct dentry *target_dentry, unsigned int flags) 227562306a36Sopenharmony_ci{ 227662306a36Sopenharmony_ci const char *from_name, *to_name; 227762306a36Sopenharmony_ci void *page1, *page2; 227862306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb; 227962306a36Sopenharmony_ci struct tcon_link *tlink; 228062306a36Sopenharmony_ci struct cifs_tcon *tcon; 228162306a36Sopenharmony_ci unsigned int xid; 228262306a36Sopenharmony_ci int rc, tmprc; 228362306a36Sopenharmony_ci int retry_count = 0; 228462306a36Sopenharmony_ci FILE_UNIX_BASIC_INFO *info_buf_source = NULL; 228562306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY 228662306a36Sopenharmony_ci FILE_UNIX_BASIC_INFO *info_buf_target; 228762306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ 228862306a36Sopenharmony_ci 228962306a36Sopenharmony_ci if (flags & ~RENAME_NOREPLACE) 229062306a36Sopenharmony_ci return -EINVAL; 229162306a36Sopenharmony_ci 229262306a36Sopenharmony_ci cifs_sb = CIFS_SB(source_dir->i_sb); 229362306a36Sopenharmony_ci if (unlikely(cifs_forced_shutdown(cifs_sb))) 229462306a36Sopenharmony_ci return -EIO; 229562306a36Sopenharmony_ci 229662306a36Sopenharmony_ci tlink = cifs_sb_tlink(cifs_sb); 229762306a36Sopenharmony_ci if (IS_ERR(tlink)) 229862306a36Sopenharmony_ci return PTR_ERR(tlink); 229962306a36Sopenharmony_ci tcon = tlink_tcon(tlink); 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_ci page1 = alloc_dentry_path(); 230262306a36Sopenharmony_ci page2 = alloc_dentry_path(); 230362306a36Sopenharmony_ci xid = get_xid(); 230462306a36Sopenharmony_ci 230562306a36Sopenharmony_ci from_name = build_path_from_dentry(source_dentry, page1); 230662306a36Sopenharmony_ci if (IS_ERR(from_name)) { 230762306a36Sopenharmony_ci rc = PTR_ERR(from_name); 230862306a36Sopenharmony_ci goto cifs_rename_exit; 230962306a36Sopenharmony_ci } 231062306a36Sopenharmony_ci 231162306a36Sopenharmony_ci to_name = build_path_from_dentry(target_dentry, page2); 231262306a36Sopenharmony_ci if (IS_ERR(to_name)) { 231362306a36Sopenharmony_ci rc = PTR_ERR(to_name); 231462306a36Sopenharmony_ci goto cifs_rename_exit; 231562306a36Sopenharmony_ci } 231662306a36Sopenharmony_ci 231762306a36Sopenharmony_ci cifs_close_deferred_file_under_dentry(tcon, from_name); 231862306a36Sopenharmony_ci if (d_inode(target_dentry) != NULL) 231962306a36Sopenharmony_ci cifs_close_deferred_file_under_dentry(tcon, to_name); 232062306a36Sopenharmony_ci 232162306a36Sopenharmony_ci rc = cifs_do_rename(xid, source_dentry, from_name, target_dentry, 232262306a36Sopenharmony_ci to_name); 232362306a36Sopenharmony_ci 232462306a36Sopenharmony_ci if (rc == -EACCES) { 232562306a36Sopenharmony_ci while (retry_count < 3) { 232662306a36Sopenharmony_ci cifs_close_all_deferred_files(tcon); 232762306a36Sopenharmony_ci rc = cifs_do_rename(xid, source_dentry, from_name, target_dentry, 232862306a36Sopenharmony_ci to_name); 232962306a36Sopenharmony_ci if (rc != -EACCES) 233062306a36Sopenharmony_ci break; 233162306a36Sopenharmony_ci retry_count++; 233262306a36Sopenharmony_ci } 233362306a36Sopenharmony_ci } 233462306a36Sopenharmony_ci 233562306a36Sopenharmony_ci /* 233662306a36Sopenharmony_ci * No-replace is the natural behavior for CIFS, so skip unlink hacks. 233762306a36Sopenharmony_ci */ 233862306a36Sopenharmony_ci if (flags & RENAME_NOREPLACE) 233962306a36Sopenharmony_ci goto cifs_rename_exit; 234062306a36Sopenharmony_ci 234162306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY 234262306a36Sopenharmony_ci if (rc == -EEXIST && tcon->unix_ext) { 234362306a36Sopenharmony_ci /* 234462306a36Sopenharmony_ci * Are src and dst hardlinks of same inode? We can only tell 234562306a36Sopenharmony_ci * with unix extensions enabled. 234662306a36Sopenharmony_ci */ 234762306a36Sopenharmony_ci info_buf_source = 234862306a36Sopenharmony_ci kmalloc_array(2, sizeof(FILE_UNIX_BASIC_INFO), 234962306a36Sopenharmony_ci GFP_KERNEL); 235062306a36Sopenharmony_ci if (info_buf_source == NULL) { 235162306a36Sopenharmony_ci rc = -ENOMEM; 235262306a36Sopenharmony_ci goto cifs_rename_exit; 235362306a36Sopenharmony_ci } 235462306a36Sopenharmony_ci 235562306a36Sopenharmony_ci info_buf_target = info_buf_source + 1; 235662306a36Sopenharmony_ci tmprc = CIFSSMBUnixQPathInfo(xid, tcon, from_name, 235762306a36Sopenharmony_ci info_buf_source, 235862306a36Sopenharmony_ci cifs_sb->local_nls, 235962306a36Sopenharmony_ci cifs_remap(cifs_sb)); 236062306a36Sopenharmony_ci if (tmprc != 0) 236162306a36Sopenharmony_ci goto unlink_target; 236262306a36Sopenharmony_ci 236362306a36Sopenharmony_ci tmprc = CIFSSMBUnixQPathInfo(xid, tcon, to_name, 236462306a36Sopenharmony_ci info_buf_target, 236562306a36Sopenharmony_ci cifs_sb->local_nls, 236662306a36Sopenharmony_ci cifs_remap(cifs_sb)); 236762306a36Sopenharmony_ci 236862306a36Sopenharmony_ci if (tmprc == 0 && (info_buf_source->UniqueId == 236962306a36Sopenharmony_ci info_buf_target->UniqueId)) { 237062306a36Sopenharmony_ci /* same file, POSIX says that this is a noop */ 237162306a36Sopenharmony_ci rc = 0; 237262306a36Sopenharmony_ci goto cifs_rename_exit; 237362306a36Sopenharmony_ci } 237462306a36Sopenharmony_ci } 237562306a36Sopenharmony_ci /* 237662306a36Sopenharmony_ci * else ... BB we could add the same check for Windows by 237762306a36Sopenharmony_ci * checking the UniqueId via FILE_INTERNAL_INFO 237862306a36Sopenharmony_ci */ 237962306a36Sopenharmony_ci 238062306a36Sopenharmony_ciunlink_target: 238162306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ 238262306a36Sopenharmony_ci 238362306a36Sopenharmony_ci /* Try unlinking the target dentry if it's not negative */ 238462306a36Sopenharmony_ci if (d_really_is_positive(target_dentry) && (rc == -EACCES || rc == -EEXIST)) { 238562306a36Sopenharmony_ci if (d_is_dir(target_dentry)) 238662306a36Sopenharmony_ci tmprc = cifs_rmdir(target_dir, target_dentry); 238762306a36Sopenharmony_ci else 238862306a36Sopenharmony_ci tmprc = cifs_unlink(target_dir, target_dentry); 238962306a36Sopenharmony_ci if (tmprc) 239062306a36Sopenharmony_ci goto cifs_rename_exit; 239162306a36Sopenharmony_ci rc = cifs_do_rename(xid, source_dentry, from_name, 239262306a36Sopenharmony_ci target_dentry, to_name); 239362306a36Sopenharmony_ci } 239462306a36Sopenharmony_ci 239562306a36Sopenharmony_ci /* force revalidate to go get info when needed */ 239662306a36Sopenharmony_ci CIFS_I(source_dir)->time = CIFS_I(target_dir)->time = 0; 239762306a36Sopenharmony_ci 239862306a36Sopenharmony_cicifs_rename_exit: 239962306a36Sopenharmony_ci kfree(info_buf_source); 240062306a36Sopenharmony_ci free_dentry_path(page2); 240162306a36Sopenharmony_ci free_dentry_path(page1); 240262306a36Sopenharmony_ci free_xid(xid); 240362306a36Sopenharmony_ci cifs_put_tlink(tlink); 240462306a36Sopenharmony_ci return rc; 240562306a36Sopenharmony_ci} 240662306a36Sopenharmony_ci 240762306a36Sopenharmony_cistatic bool 240862306a36Sopenharmony_cicifs_dentry_needs_reval(struct dentry *dentry) 240962306a36Sopenharmony_ci{ 241062306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 241162306a36Sopenharmony_ci struct cifsInodeInfo *cifs_i = CIFS_I(inode); 241262306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); 241362306a36Sopenharmony_ci struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); 241462306a36Sopenharmony_ci struct cached_fid *cfid = NULL; 241562306a36Sopenharmony_ci 241662306a36Sopenharmony_ci if (cifs_i->time == 0) 241762306a36Sopenharmony_ci return true; 241862306a36Sopenharmony_ci 241962306a36Sopenharmony_ci if (CIFS_CACHE_READ(cifs_i)) 242062306a36Sopenharmony_ci return false; 242162306a36Sopenharmony_ci 242262306a36Sopenharmony_ci if (!lookupCacheEnabled) 242362306a36Sopenharmony_ci return true; 242462306a36Sopenharmony_ci 242562306a36Sopenharmony_ci if (!open_cached_dir_by_dentry(tcon, dentry->d_parent, &cfid)) { 242662306a36Sopenharmony_ci spin_lock(&cfid->fid_lock); 242762306a36Sopenharmony_ci if (cfid->time && cifs_i->time > cfid->time) { 242862306a36Sopenharmony_ci spin_unlock(&cfid->fid_lock); 242962306a36Sopenharmony_ci close_cached_dir(cfid); 243062306a36Sopenharmony_ci return false; 243162306a36Sopenharmony_ci } 243262306a36Sopenharmony_ci spin_unlock(&cfid->fid_lock); 243362306a36Sopenharmony_ci close_cached_dir(cfid); 243462306a36Sopenharmony_ci } 243562306a36Sopenharmony_ci /* 243662306a36Sopenharmony_ci * depending on inode type, check if attribute caching disabled for 243762306a36Sopenharmony_ci * files or directories 243862306a36Sopenharmony_ci */ 243962306a36Sopenharmony_ci if (S_ISDIR(inode->i_mode)) { 244062306a36Sopenharmony_ci if (!cifs_sb->ctx->acdirmax) 244162306a36Sopenharmony_ci return true; 244262306a36Sopenharmony_ci if (!time_in_range(jiffies, cifs_i->time, 244362306a36Sopenharmony_ci cifs_i->time + cifs_sb->ctx->acdirmax)) 244462306a36Sopenharmony_ci return true; 244562306a36Sopenharmony_ci } else { /* file */ 244662306a36Sopenharmony_ci if (!cifs_sb->ctx->acregmax) 244762306a36Sopenharmony_ci return true; 244862306a36Sopenharmony_ci if (!time_in_range(jiffies, cifs_i->time, 244962306a36Sopenharmony_ci cifs_i->time + cifs_sb->ctx->acregmax)) 245062306a36Sopenharmony_ci return true; 245162306a36Sopenharmony_ci } 245262306a36Sopenharmony_ci 245362306a36Sopenharmony_ci /* hardlinked files w/ noserverino get "special" treatment */ 245462306a36Sopenharmony_ci if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) && 245562306a36Sopenharmony_ci S_ISREG(inode->i_mode) && inode->i_nlink != 1) 245662306a36Sopenharmony_ci return true; 245762306a36Sopenharmony_ci 245862306a36Sopenharmony_ci return false; 245962306a36Sopenharmony_ci} 246062306a36Sopenharmony_ci 246162306a36Sopenharmony_ci/* 246262306a36Sopenharmony_ci * Zap the cache. Called when invalid_mapping flag is set. 246362306a36Sopenharmony_ci */ 246462306a36Sopenharmony_ciint 246562306a36Sopenharmony_cicifs_invalidate_mapping(struct inode *inode) 246662306a36Sopenharmony_ci{ 246762306a36Sopenharmony_ci int rc = 0; 246862306a36Sopenharmony_ci 246962306a36Sopenharmony_ci if (inode->i_mapping && inode->i_mapping->nrpages != 0) { 247062306a36Sopenharmony_ci rc = invalidate_inode_pages2(inode->i_mapping); 247162306a36Sopenharmony_ci if (rc) 247262306a36Sopenharmony_ci cifs_dbg(VFS, "%s: invalidate inode %p failed with rc %d\n", 247362306a36Sopenharmony_ci __func__, inode, rc); 247462306a36Sopenharmony_ci } 247562306a36Sopenharmony_ci 247662306a36Sopenharmony_ci return rc; 247762306a36Sopenharmony_ci} 247862306a36Sopenharmony_ci 247962306a36Sopenharmony_ci/** 248062306a36Sopenharmony_ci * cifs_wait_bit_killable - helper for functions that are sleeping on bit locks 248162306a36Sopenharmony_ci * 248262306a36Sopenharmony_ci * @key: currently unused 248362306a36Sopenharmony_ci * @mode: the task state to sleep in 248462306a36Sopenharmony_ci */ 248562306a36Sopenharmony_cistatic int 248662306a36Sopenharmony_cicifs_wait_bit_killable(struct wait_bit_key *key, int mode) 248762306a36Sopenharmony_ci{ 248862306a36Sopenharmony_ci schedule(); 248962306a36Sopenharmony_ci if (signal_pending_state(mode, current)) 249062306a36Sopenharmony_ci return -ERESTARTSYS; 249162306a36Sopenharmony_ci return 0; 249262306a36Sopenharmony_ci} 249362306a36Sopenharmony_ci 249462306a36Sopenharmony_ciint 249562306a36Sopenharmony_cicifs_revalidate_mapping(struct inode *inode) 249662306a36Sopenharmony_ci{ 249762306a36Sopenharmony_ci int rc; 249862306a36Sopenharmony_ci unsigned long *flags = &CIFS_I(inode)->flags; 249962306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); 250062306a36Sopenharmony_ci 250162306a36Sopenharmony_ci /* swapfiles are not supposed to be shared */ 250262306a36Sopenharmony_ci if (IS_SWAPFILE(inode)) 250362306a36Sopenharmony_ci return 0; 250462306a36Sopenharmony_ci 250562306a36Sopenharmony_ci rc = wait_on_bit_lock_action(flags, CIFS_INO_LOCK, cifs_wait_bit_killable, 250662306a36Sopenharmony_ci TASK_KILLABLE|TASK_FREEZABLE_UNSAFE); 250762306a36Sopenharmony_ci if (rc) 250862306a36Sopenharmony_ci return rc; 250962306a36Sopenharmony_ci 251062306a36Sopenharmony_ci if (test_and_clear_bit(CIFS_INO_INVALID_MAPPING, flags)) { 251162306a36Sopenharmony_ci /* for cache=singleclient, do not invalidate */ 251262306a36Sopenharmony_ci if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RW_CACHE) 251362306a36Sopenharmony_ci goto skip_invalidate; 251462306a36Sopenharmony_ci 251562306a36Sopenharmony_ci rc = cifs_invalidate_mapping(inode); 251662306a36Sopenharmony_ci if (rc) 251762306a36Sopenharmony_ci set_bit(CIFS_INO_INVALID_MAPPING, flags); 251862306a36Sopenharmony_ci } 251962306a36Sopenharmony_ci 252062306a36Sopenharmony_ciskip_invalidate: 252162306a36Sopenharmony_ci clear_bit_unlock(CIFS_INO_LOCK, flags); 252262306a36Sopenharmony_ci smp_mb__after_atomic(); 252362306a36Sopenharmony_ci wake_up_bit(flags, CIFS_INO_LOCK); 252462306a36Sopenharmony_ci 252562306a36Sopenharmony_ci return rc; 252662306a36Sopenharmony_ci} 252762306a36Sopenharmony_ci 252862306a36Sopenharmony_ciint 252962306a36Sopenharmony_cicifs_zap_mapping(struct inode *inode) 253062306a36Sopenharmony_ci{ 253162306a36Sopenharmony_ci set_bit(CIFS_INO_INVALID_MAPPING, &CIFS_I(inode)->flags); 253262306a36Sopenharmony_ci return cifs_revalidate_mapping(inode); 253362306a36Sopenharmony_ci} 253462306a36Sopenharmony_ci 253562306a36Sopenharmony_ciint cifs_revalidate_file_attr(struct file *filp) 253662306a36Sopenharmony_ci{ 253762306a36Sopenharmony_ci int rc = 0; 253862306a36Sopenharmony_ci struct dentry *dentry = file_dentry(filp); 253962306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY 254062306a36Sopenharmony_ci struct cifsFileInfo *cfile = (struct cifsFileInfo *) filp->private_data; 254162306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ 254262306a36Sopenharmony_ci 254362306a36Sopenharmony_ci if (!cifs_dentry_needs_reval(dentry)) 254462306a36Sopenharmony_ci return rc; 254562306a36Sopenharmony_ci 254662306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY 254762306a36Sopenharmony_ci if (tlink_tcon(cfile->tlink)->unix_ext) 254862306a36Sopenharmony_ci rc = cifs_get_file_info_unix(filp); 254962306a36Sopenharmony_ci else 255062306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ 255162306a36Sopenharmony_ci rc = cifs_get_file_info(filp); 255262306a36Sopenharmony_ci 255362306a36Sopenharmony_ci return rc; 255462306a36Sopenharmony_ci} 255562306a36Sopenharmony_ci 255662306a36Sopenharmony_ciint cifs_revalidate_dentry_attr(struct dentry *dentry) 255762306a36Sopenharmony_ci{ 255862306a36Sopenharmony_ci unsigned int xid; 255962306a36Sopenharmony_ci int rc = 0; 256062306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 256162306a36Sopenharmony_ci struct super_block *sb = dentry->d_sb; 256262306a36Sopenharmony_ci const char *full_path; 256362306a36Sopenharmony_ci void *page; 256462306a36Sopenharmony_ci int count = 0; 256562306a36Sopenharmony_ci 256662306a36Sopenharmony_ci if (inode == NULL) 256762306a36Sopenharmony_ci return -ENOENT; 256862306a36Sopenharmony_ci 256962306a36Sopenharmony_ci if (!cifs_dentry_needs_reval(dentry)) 257062306a36Sopenharmony_ci return rc; 257162306a36Sopenharmony_ci 257262306a36Sopenharmony_ci xid = get_xid(); 257362306a36Sopenharmony_ci 257462306a36Sopenharmony_ci page = alloc_dentry_path(); 257562306a36Sopenharmony_ci full_path = build_path_from_dentry(dentry, page); 257662306a36Sopenharmony_ci if (IS_ERR(full_path)) { 257762306a36Sopenharmony_ci rc = PTR_ERR(full_path); 257862306a36Sopenharmony_ci goto out; 257962306a36Sopenharmony_ci } 258062306a36Sopenharmony_ci 258162306a36Sopenharmony_ci cifs_dbg(FYI, "Update attributes: %s inode 0x%p count %d dentry: 0x%p d_time %ld jiffies %ld\n", 258262306a36Sopenharmony_ci full_path, inode, inode->i_count.counter, 258362306a36Sopenharmony_ci dentry, cifs_get_time(dentry), jiffies); 258462306a36Sopenharmony_ci 258562306a36Sopenharmony_ciagain: 258662306a36Sopenharmony_ci if (cifs_sb_master_tcon(CIFS_SB(sb))->posix_extensions) 258762306a36Sopenharmony_ci rc = smb311_posix_get_inode_info(&inode, full_path, sb, xid); 258862306a36Sopenharmony_ci else if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext) 258962306a36Sopenharmony_ci rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid); 259062306a36Sopenharmony_ci else 259162306a36Sopenharmony_ci rc = cifs_get_inode_info(&inode, full_path, NULL, sb, 259262306a36Sopenharmony_ci xid, NULL); 259362306a36Sopenharmony_ci if (rc == -EAGAIN && count++ < 10) 259462306a36Sopenharmony_ci goto again; 259562306a36Sopenharmony_ciout: 259662306a36Sopenharmony_ci free_dentry_path(page); 259762306a36Sopenharmony_ci free_xid(xid); 259862306a36Sopenharmony_ci 259962306a36Sopenharmony_ci return rc; 260062306a36Sopenharmony_ci} 260162306a36Sopenharmony_ci 260262306a36Sopenharmony_ciint cifs_revalidate_file(struct file *filp) 260362306a36Sopenharmony_ci{ 260462306a36Sopenharmony_ci int rc; 260562306a36Sopenharmony_ci struct inode *inode = file_inode(filp); 260662306a36Sopenharmony_ci 260762306a36Sopenharmony_ci rc = cifs_revalidate_file_attr(filp); 260862306a36Sopenharmony_ci if (rc) 260962306a36Sopenharmony_ci return rc; 261062306a36Sopenharmony_ci 261162306a36Sopenharmony_ci return cifs_revalidate_mapping(inode); 261262306a36Sopenharmony_ci} 261362306a36Sopenharmony_ci 261462306a36Sopenharmony_ci/* revalidate a dentry's inode attributes */ 261562306a36Sopenharmony_ciint cifs_revalidate_dentry(struct dentry *dentry) 261662306a36Sopenharmony_ci{ 261762306a36Sopenharmony_ci int rc; 261862306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 261962306a36Sopenharmony_ci 262062306a36Sopenharmony_ci rc = cifs_revalidate_dentry_attr(dentry); 262162306a36Sopenharmony_ci if (rc) 262262306a36Sopenharmony_ci return rc; 262362306a36Sopenharmony_ci 262462306a36Sopenharmony_ci return cifs_revalidate_mapping(inode); 262562306a36Sopenharmony_ci} 262662306a36Sopenharmony_ci 262762306a36Sopenharmony_ciint cifs_getattr(struct mnt_idmap *idmap, const struct path *path, 262862306a36Sopenharmony_ci struct kstat *stat, u32 request_mask, unsigned int flags) 262962306a36Sopenharmony_ci{ 263062306a36Sopenharmony_ci struct dentry *dentry = path->dentry; 263162306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb = CIFS_SB(dentry->d_sb); 263262306a36Sopenharmony_ci struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); 263362306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 263462306a36Sopenharmony_ci int rc; 263562306a36Sopenharmony_ci 263662306a36Sopenharmony_ci if (unlikely(cifs_forced_shutdown(CIFS_SB(inode->i_sb)))) 263762306a36Sopenharmony_ci return -EIO; 263862306a36Sopenharmony_ci 263962306a36Sopenharmony_ci /* 264062306a36Sopenharmony_ci * We need to be sure that all dirty pages are written and the server 264162306a36Sopenharmony_ci * has actual ctime, mtime and file length. 264262306a36Sopenharmony_ci */ 264362306a36Sopenharmony_ci if ((request_mask & (STATX_CTIME | STATX_MTIME | STATX_SIZE | STATX_BLOCKS)) && 264462306a36Sopenharmony_ci !CIFS_CACHE_READ(CIFS_I(inode)) && 264562306a36Sopenharmony_ci inode->i_mapping && inode->i_mapping->nrpages != 0) { 264662306a36Sopenharmony_ci rc = filemap_fdatawait(inode->i_mapping); 264762306a36Sopenharmony_ci if (rc) { 264862306a36Sopenharmony_ci mapping_set_error(inode->i_mapping, rc); 264962306a36Sopenharmony_ci return rc; 265062306a36Sopenharmony_ci } 265162306a36Sopenharmony_ci } 265262306a36Sopenharmony_ci 265362306a36Sopenharmony_ci if ((flags & AT_STATX_SYNC_TYPE) == AT_STATX_FORCE_SYNC) 265462306a36Sopenharmony_ci CIFS_I(inode)->time = 0; /* force revalidate */ 265562306a36Sopenharmony_ci 265662306a36Sopenharmony_ci /* 265762306a36Sopenharmony_ci * If the caller doesn't require syncing, only sync if 265862306a36Sopenharmony_ci * necessary (e.g. due to earlier truncate or setattr 265962306a36Sopenharmony_ci * invalidating the cached metadata) 266062306a36Sopenharmony_ci */ 266162306a36Sopenharmony_ci if (((flags & AT_STATX_SYNC_TYPE) != AT_STATX_DONT_SYNC) || 266262306a36Sopenharmony_ci (CIFS_I(inode)->time == 0)) { 266362306a36Sopenharmony_ci rc = cifs_revalidate_dentry_attr(dentry); 266462306a36Sopenharmony_ci if (rc) 266562306a36Sopenharmony_ci return rc; 266662306a36Sopenharmony_ci } 266762306a36Sopenharmony_ci 266862306a36Sopenharmony_ci generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat); 266962306a36Sopenharmony_ci stat->blksize = cifs_sb->ctx->bsize; 267062306a36Sopenharmony_ci stat->ino = CIFS_I(inode)->uniqueid; 267162306a36Sopenharmony_ci 267262306a36Sopenharmony_ci /* old CIFS Unix Extensions doesn't return create time */ 267362306a36Sopenharmony_ci if (CIFS_I(inode)->createtime) { 267462306a36Sopenharmony_ci stat->result_mask |= STATX_BTIME; 267562306a36Sopenharmony_ci stat->btime = 267662306a36Sopenharmony_ci cifs_NTtimeToUnix(cpu_to_le64(CIFS_I(inode)->createtime)); 267762306a36Sopenharmony_ci } 267862306a36Sopenharmony_ci 267962306a36Sopenharmony_ci stat->attributes_mask |= (STATX_ATTR_COMPRESSED | STATX_ATTR_ENCRYPTED); 268062306a36Sopenharmony_ci if (CIFS_I(inode)->cifsAttrs & FILE_ATTRIBUTE_COMPRESSED) 268162306a36Sopenharmony_ci stat->attributes |= STATX_ATTR_COMPRESSED; 268262306a36Sopenharmony_ci if (CIFS_I(inode)->cifsAttrs & FILE_ATTRIBUTE_ENCRYPTED) 268362306a36Sopenharmony_ci stat->attributes |= STATX_ATTR_ENCRYPTED; 268462306a36Sopenharmony_ci 268562306a36Sopenharmony_ci /* 268662306a36Sopenharmony_ci * If on a multiuser mount without unix extensions or cifsacl being 268762306a36Sopenharmony_ci * enabled, and the admin hasn't overridden them, set the ownership 268862306a36Sopenharmony_ci * to the fsuid/fsgid of the current process. 268962306a36Sopenharmony_ci */ 269062306a36Sopenharmony_ci if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER) && 269162306a36Sopenharmony_ci !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) && 269262306a36Sopenharmony_ci !tcon->unix_ext) { 269362306a36Sopenharmony_ci if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)) 269462306a36Sopenharmony_ci stat->uid = current_fsuid(); 269562306a36Sopenharmony_ci if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)) 269662306a36Sopenharmony_ci stat->gid = current_fsgid(); 269762306a36Sopenharmony_ci } 269862306a36Sopenharmony_ci return 0; 269962306a36Sopenharmony_ci} 270062306a36Sopenharmony_ci 270162306a36Sopenharmony_ciint cifs_fiemap(struct inode *inode, struct fiemap_extent_info *fei, u64 start, 270262306a36Sopenharmony_ci u64 len) 270362306a36Sopenharmony_ci{ 270462306a36Sopenharmony_ci struct cifsInodeInfo *cifs_i = CIFS_I(inode); 270562306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_i->netfs.inode.i_sb); 270662306a36Sopenharmony_ci struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); 270762306a36Sopenharmony_ci struct TCP_Server_Info *server = tcon->ses->server; 270862306a36Sopenharmony_ci struct cifsFileInfo *cfile; 270962306a36Sopenharmony_ci int rc; 271062306a36Sopenharmony_ci 271162306a36Sopenharmony_ci if (unlikely(cifs_forced_shutdown(cifs_sb))) 271262306a36Sopenharmony_ci return -EIO; 271362306a36Sopenharmony_ci 271462306a36Sopenharmony_ci /* 271562306a36Sopenharmony_ci * We need to be sure that all dirty pages are written as they 271662306a36Sopenharmony_ci * might fill holes on the server. 271762306a36Sopenharmony_ci */ 271862306a36Sopenharmony_ci if (!CIFS_CACHE_READ(CIFS_I(inode)) && inode->i_mapping && 271962306a36Sopenharmony_ci inode->i_mapping->nrpages != 0) { 272062306a36Sopenharmony_ci rc = filemap_fdatawait(inode->i_mapping); 272162306a36Sopenharmony_ci if (rc) { 272262306a36Sopenharmony_ci mapping_set_error(inode->i_mapping, rc); 272362306a36Sopenharmony_ci return rc; 272462306a36Sopenharmony_ci } 272562306a36Sopenharmony_ci } 272662306a36Sopenharmony_ci 272762306a36Sopenharmony_ci cfile = find_readable_file(cifs_i, false); 272862306a36Sopenharmony_ci if (cfile == NULL) 272962306a36Sopenharmony_ci return -EINVAL; 273062306a36Sopenharmony_ci 273162306a36Sopenharmony_ci if (server->ops->fiemap) { 273262306a36Sopenharmony_ci rc = server->ops->fiemap(tcon, cfile, fei, start, len); 273362306a36Sopenharmony_ci cifsFileInfo_put(cfile); 273462306a36Sopenharmony_ci return rc; 273562306a36Sopenharmony_ci } 273662306a36Sopenharmony_ci 273762306a36Sopenharmony_ci cifsFileInfo_put(cfile); 273862306a36Sopenharmony_ci return -EOPNOTSUPP; 273962306a36Sopenharmony_ci} 274062306a36Sopenharmony_ci 274162306a36Sopenharmony_ciint cifs_truncate_page(struct address_space *mapping, loff_t from) 274262306a36Sopenharmony_ci{ 274362306a36Sopenharmony_ci pgoff_t index = from >> PAGE_SHIFT; 274462306a36Sopenharmony_ci unsigned offset = from & (PAGE_SIZE - 1); 274562306a36Sopenharmony_ci struct page *page; 274662306a36Sopenharmony_ci int rc = 0; 274762306a36Sopenharmony_ci 274862306a36Sopenharmony_ci page = grab_cache_page(mapping, index); 274962306a36Sopenharmony_ci if (!page) 275062306a36Sopenharmony_ci return -ENOMEM; 275162306a36Sopenharmony_ci 275262306a36Sopenharmony_ci zero_user_segment(page, offset, PAGE_SIZE); 275362306a36Sopenharmony_ci unlock_page(page); 275462306a36Sopenharmony_ci put_page(page); 275562306a36Sopenharmony_ci return rc; 275662306a36Sopenharmony_ci} 275762306a36Sopenharmony_ci 275862306a36Sopenharmony_civoid cifs_setsize(struct inode *inode, loff_t offset) 275962306a36Sopenharmony_ci{ 276062306a36Sopenharmony_ci struct cifsInodeInfo *cifs_i = CIFS_I(inode); 276162306a36Sopenharmony_ci 276262306a36Sopenharmony_ci spin_lock(&inode->i_lock); 276362306a36Sopenharmony_ci i_size_write(inode, offset); 276462306a36Sopenharmony_ci spin_unlock(&inode->i_lock); 276562306a36Sopenharmony_ci 276662306a36Sopenharmony_ci /* Cached inode must be refreshed on truncate */ 276762306a36Sopenharmony_ci cifs_i->time = 0; 276862306a36Sopenharmony_ci truncate_pagecache(inode, offset); 276962306a36Sopenharmony_ci} 277062306a36Sopenharmony_ci 277162306a36Sopenharmony_cistatic int 277262306a36Sopenharmony_cicifs_set_file_size(struct inode *inode, struct iattr *attrs, 277362306a36Sopenharmony_ci unsigned int xid, const char *full_path) 277462306a36Sopenharmony_ci{ 277562306a36Sopenharmony_ci int rc; 277662306a36Sopenharmony_ci struct cifsFileInfo *open_file; 277762306a36Sopenharmony_ci struct cifsInodeInfo *cifsInode = CIFS_I(inode); 277862306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); 277962306a36Sopenharmony_ci struct tcon_link *tlink = NULL; 278062306a36Sopenharmony_ci struct cifs_tcon *tcon = NULL; 278162306a36Sopenharmony_ci struct TCP_Server_Info *server; 278262306a36Sopenharmony_ci 278362306a36Sopenharmony_ci /* 278462306a36Sopenharmony_ci * To avoid spurious oplock breaks from server, in the case of 278562306a36Sopenharmony_ci * inodes that we already have open, avoid doing path based 278662306a36Sopenharmony_ci * setting of file size if we can do it by handle. 278762306a36Sopenharmony_ci * This keeps our caching token (oplock) and avoids timeouts 278862306a36Sopenharmony_ci * when the local oplock break takes longer to flush 278962306a36Sopenharmony_ci * writebehind data than the SMB timeout for the SetPathInfo 279062306a36Sopenharmony_ci * request would allow 279162306a36Sopenharmony_ci */ 279262306a36Sopenharmony_ci open_file = find_writable_file(cifsInode, FIND_WR_FSUID_ONLY); 279362306a36Sopenharmony_ci if (open_file) { 279462306a36Sopenharmony_ci tcon = tlink_tcon(open_file->tlink); 279562306a36Sopenharmony_ci server = tcon->ses->server; 279662306a36Sopenharmony_ci if (server->ops->set_file_size) 279762306a36Sopenharmony_ci rc = server->ops->set_file_size(xid, tcon, open_file, 279862306a36Sopenharmony_ci attrs->ia_size, false); 279962306a36Sopenharmony_ci else 280062306a36Sopenharmony_ci rc = -ENOSYS; 280162306a36Sopenharmony_ci cifsFileInfo_put(open_file); 280262306a36Sopenharmony_ci cifs_dbg(FYI, "SetFSize for attrs rc = %d\n", rc); 280362306a36Sopenharmony_ci } else 280462306a36Sopenharmony_ci rc = -EINVAL; 280562306a36Sopenharmony_ci 280662306a36Sopenharmony_ci if (!rc) 280762306a36Sopenharmony_ci goto set_size_out; 280862306a36Sopenharmony_ci 280962306a36Sopenharmony_ci if (tcon == NULL) { 281062306a36Sopenharmony_ci tlink = cifs_sb_tlink(cifs_sb); 281162306a36Sopenharmony_ci if (IS_ERR(tlink)) 281262306a36Sopenharmony_ci return PTR_ERR(tlink); 281362306a36Sopenharmony_ci tcon = tlink_tcon(tlink); 281462306a36Sopenharmony_ci server = tcon->ses->server; 281562306a36Sopenharmony_ci } 281662306a36Sopenharmony_ci 281762306a36Sopenharmony_ci /* 281862306a36Sopenharmony_ci * Set file size by pathname rather than by handle either because no 281962306a36Sopenharmony_ci * valid, writeable file handle for it was found or because there was 282062306a36Sopenharmony_ci * an error setting it by handle. 282162306a36Sopenharmony_ci */ 282262306a36Sopenharmony_ci if (server->ops->set_path_size) 282362306a36Sopenharmony_ci rc = server->ops->set_path_size(xid, tcon, full_path, 282462306a36Sopenharmony_ci attrs->ia_size, cifs_sb, false); 282562306a36Sopenharmony_ci else 282662306a36Sopenharmony_ci rc = -ENOSYS; 282762306a36Sopenharmony_ci cifs_dbg(FYI, "SetEOF by path (setattrs) rc = %d\n", rc); 282862306a36Sopenharmony_ci 282962306a36Sopenharmony_ci if (tlink) 283062306a36Sopenharmony_ci cifs_put_tlink(tlink); 283162306a36Sopenharmony_ci 283262306a36Sopenharmony_ciset_size_out: 283362306a36Sopenharmony_ci if (rc == 0) { 283462306a36Sopenharmony_ci cifsInode->server_eof = attrs->ia_size; 283562306a36Sopenharmony_ci cifs_setsize(inode, attrs->ia_size); 283662306a36Sopenharmony_ci /* 283762306a36Sopenharmony_ci * i_blocks is not related to (i_size / i_blksize), but instead 283862306a36Sopenharmony_ci * 512 byte (2**9) size is required for calculating num blocks. 283962306a36Sopenharmony_ci * Until we can query the server for actual allocation size, 284062306a36Sopenharmony_ci * this is best estimate we have for blocks allocated for a file 284162306a36Sopenharmony_ci * Number of blocks must be rounded up so size 1 is not 0 blocks 284262306a36Sopenharmony_ci */ 284362306a36Sopenharmony_ci inode->i_blocks = (512 - 1 + attrs->ia_size) >> 9; 284462306a36Sopenharmony_ci 284562306a36Sopenharmony_ci /* 284662306a36Sopenharmony_ci * The man page of truncate says if the size changed, 284762306a36Sopenharmony_ci * then the st_ctime and st_mtime fields for the file 284862306a36Sopenharmony_ci * are updated. 284962306a36Sopenharmony_ci */ 285062306a36Sopenharmony_ci attrs->ia_ctime = attrs->ia_mtime = current_time(inode); 285162306a36Sopenharmony_ci attrs->ia_valid |= ATTR_CTIME | ATTR_MTIME; 285262306a36Sopenharmony_ci 285362306a36Sopenharmony_ci cifs_truncate_page(inode->i_mapping, inode->i_size); 285462306a36Sopenharmony_ci } 285562306a36Sopenharmony_ci 285662306a36Sopenharmony_ci return rc; 285762306a36Sopenharmony_ci} 285862306a36Sopenharmony_ci 285962306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY 286062306a36Sopenharmony_cistatic int 286162306a36Sopenharmony_cicifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) 286262306a36Sopenharmony_ci{ 286362306a36Sopenharmony_ci int rc; 286462306a36Sopenharmony_ci unsigned int xid; 286562306a36Sopenharmony_ci const char *full_path; 286662306a36Sopenharmony_ci void *page = alloc_dentry_path(); 286762306a36Sopenharmony_ci struct inode *inode = d_inode(direntry); 286862306a36Sopenharmony_ci struct cifsInodeInfo *cifsInode = CIFS_I(inode); 286962306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); 287062306a36Sopenharmony_ci struct tcon_link *tlink; 287162306a36Sopenharmony_ci struct cifs_tcon *pTcon; 287262306a36Sopenharmony_ci struct cifs_unix_set_info_args *args = NULL; 287362306a36Sopenharmony_ci struct cifsFileInfo *open_file; 287462306a36Sopenharmony_ci 287562306a36Sopenharmony_ci cifs_dbg(FYI, "setattr_unix on file %pd attrs->ia_valid=0x%x\n", 287662306a36Sopenharmony_ci direntry, attrs->ia_valid); 287762306a36Sopenharmony_ci 287862306a36Sopenharmony_ci xid = get_xid(); 287962306a36Sopenharmony_ci 288062306a36Sopenharmony_ci if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) 288162306a36Sopenharmony_ci attrs->ia_valid |= ATTR_FORCE; 288262306a36Sopenharmony_ci 288362306a36Sopenharmony_ci rc = setattr_prepare(&nop_mnt_idmap, direntry, attrs); 288462306a36Sopenharmony_ci if (rc < 0) 288562306a36Sopenharmony_ci goto out; 288662306a36Sopenharmony_ci 288762306a36Sopenharmony_ci full_path = build_path_from_dentry(direntry, page); 288862306a36Sopenharmony_ci if (IS_ERR(full_path)) { 288962306a36Sopenharmony_ci rc = PTR_ERR(full_path); 289062306a36Sopenharmony_ci goto out; 289162306a36Sopenharmony_ci } 289262306a36Sopenharmony_ci 289362306a36Sopenharmony_ci /* 289462306a36Sopenharmony_ci * Attempt to flush data before changing attributes. We need to do 289562306a36Sopenharmony_ci * this for ATTR_SIZE and ATTR_MTIME for sure, and if we change the 289662306a36Sopenharmony_ci * ownership or mode then we may also need to do this. Here, we take 289762306a36Sopenharmony_ci * the safe way out and just do the flush on all setattr requests. If 289862306a36Sopenharmony_ci * the flush returns error, store it to report later and continue. 289962306a36Sopenharmony_ci * 290062306a36Sopenharmony_ci * BB: This should be smarter. Why bother flushing pages that 290162306a36Sopenharmony_ci * will be truncated anyway? Also, should we error out here if 290262306a36Sopenharmony_ci * the flush returns error? 290362306a36Sopenharmony_ci */ 290462306a36Sopenharmony_ci rc = filemap_write_and_wait(inode->i_mapping); 290562306a36Sopenharmony_ci if (is_interrupt_error(rc)) { 290662306a36Sopenharmony_ci rc = -ERESTARTSYS; 290762306a36Sopenharmony_ci goto out; 290862306a36Sopenharmony_ci } 290962306a36Sopenharmony_ci 291062306a36Sopenharmony_ci mapping_set_error(inode->i_mapping, rc); 291162306a36Sopenharmony_ci rc = 0; 291262306a36Sopenharmony_ci 291362306a36Sopenharmony_ci if (attrs->ia_valid & ATTR_SIZE) { 291462306a36Sopenharmony_ci rc = cifs_set_file_size(inode, attrs, xid, full_path); 291562306a36Sopenharmony_ci if (rc != 0) 291662306a36Sopenharmony_ci goto out; 291762306a36Sopenharmony_ci } 291862306a36Sopenharmony_ci 291962306a36Sopenharmony_ci /* skip mode change if it's just for clearing setuid/setgid */ 292062306a36Sopenharmony_ci if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID)) 292162306a36Sopenharmony_ci attrs->ia_valid &= ~ATTR_MODE; 292262306a36Sopenharmony_ci 292362306a36Sopenharmony_ci args = kmalloc(sizeof(*args), GFP_KERNEL); 292462306a36Sopenharmony_ci if (args == NULL) { 292562306a36Sopenharmony_ci rc = -ENOMEM; 292662306a36Sopenharmony_ci goto out; 292762306a36Sopenharmony_ci } 292862306a36Sopenharmony_ci 292962306a36Sopenharmony_ci /* set up the struct */ 293062306a36Sopenharmony_ci if (attrs->ia_valid & ATTR_MODE) 293162306a36Sopenharmony_ci args->mode = attrs->ia_mode; 293262306a36Sopenharmony_ci else 293362306a36Sopenharmony_ci args->mode = NO_CHANGE_64; 293462306a36Sopenharmony_ci 293562306a36Sopenharmony_ci if (attrs->ia_valid & ATTR_UID) 293662306a36Sopenharmony_ci args->uid = attrs->ia_uid; 293762306a36Sopenharmony_ci else 293862306a36Sopenharmony_ci args->uid = INVALID_UID; /* no change */ 293962306a36Sopenharmony_ci 294062306a36Sopenharmony_ci if (attrs->ia_valid & ATTR_GID) 294162306a36Sopenharmony_ci args->gid = attrs->ia_gid; 294262306a36Sopenharmony_ci else 294362306a36Sopenharmony_ci args->gid = INVALID_GID; /* no change */ 294462306a36Sopenharmony_ci 294562306a36Sopenharmony_ci if (attrs->ia_valid & ATTR_ATIME) 294662306a36Sopenharmony_ci args->atime = cifs_UnixTimeToNT(attrs->ia_atime); 294762306a36Sopenharmony_ci else 294862306a36Sopenharmony_ci args->atime = NO_CHANGE_64; 294962306a36Sopenharmony_ci 295062306a36Sopenharmony_ci if (attrs->ia_valid & ATTR_MTIME) 295162306a36Sopenharmony_ci args->mtime = cifs_UnixTimeToNT(attrs->ia_mtime); 295262306a36Sopenharmony_ci else 295362306a36Sopenharmony_ci args->mtime = NO_CHANGE_64; 295462306a36Sopenharmony_ci 295562306a36Sopenharmony_ci if (attrs->ia_valid & ATTR_CTIME) 295662306a36Sopenharmony_ci args->ctime = cifs_UnixTimeToNT(attrs->ia_ctime); 295762306a36Sopenharmony_ci else 295862306a36Sopenharmony_ci args->ctime = NO_CHANGE_64; 295962306a36Sopenharmony_ci 296062306a36Sopenharmony_ci args->device = 0; 296162306a36Sopenharmony_ci open_file = find_writable_file(cifsInode, FIND_WR_FSUID_ONLY); 296262306a36Sopenharmony_ci if (open_file) { 296362306a36Sopenharmony_ci u16 nfid = open_file->fid.netfid; 296462306a36Sopenharmony_ci u32 npid = open_file->pid; 296562306a36Sopenharmony_ci pTcon = tlink_tcon(open_file->tlink); 296662306a36Sopenharmony_ci rc = CIFSSMBUnixSetFileInfo(xid, pTcon, args, nfid, npid); 296762306a36Sopenharmony_ci cifsFileInfo_put(open_file); 296862306a36Sopenharmony_ci } else { 296962306a36Sopenharmony_ci tlink = cifs_sb_tlink(cifs_sb); 297062306a36Sopenharmony_ci if (IS_ERR(tlink)) { 297162306a36Sopenharmony_ci rc = PTR_ERR(tlink); 297262306a36Sopenharmony_ci goto out; 297362306a36Sopenharmony_ci } 297462306a36Sopenharmony_ci pTcon = tlink_tcon(tlink); 297562306a36Sopenharmony_ci rc = CIFSSMBUnixSetPathInfo(xid, pTcon, full_path, args, 297662306a36Sopenharmony_ci cifs_sb->local_nls, 297762306a36Sopenharmony_ci cifs_remap(cifs_sb)); 297862306a36Sopenharmony_ci cifs_put_tlink(tlink); 297962306a36Sopenharmony_ci } 298062306a36Sopenharmony_ci 298162306a36Sopenharmony_ci if (rc) 298262306a36Sopenharmony_ci goto out; 298362306a36Sopenharmony_ci 298462306a36Sopenharmony_ci if ((attrs->ia_valid & ATTR_SIZE) && 298562306a36Sopenharmony_ci attrs->ia_size != i_size_read(inode)) { 298662306a36Sopenharmony_ci truncate_setsize(inode, attrs->ia_size); 298762306a36Sopenharmony_ci fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size); 298862306a36Sopenharmony_ci } 298962306a36Sopenharmony_ci 299062306a36Sopenharmony_ci setattr_copy(&nop_mnt_idmap, inode, attrs); 299162306a36Sopenharmony_ci mark_inode_dirty(inode); 299262306a36Sopenharmony_ci 299362306a36Sopenharmony_ci /* force revalidate when any of these times are set since some 299462306a36Sopenharmony_ci of the fs types (eg ext3, fat) do not have fine enough 299562306a36Sopenharmony_ci time granularity to match protocol, and we do not have a 299662306a36Sopenharmony_ci a way (yet) to query the server fs's time granularity (and 299762306a36Sopenharmony_ci whether it rounds times down). 299862306a36Sopenharmony_ci */ 299962306a36Sopenharmony_ci if (attrs->ia_valid & (ATTR_MTIME | ATTR_CTIME)) 300062306a36Sopenharmony_ci cifsInode->time = 0; 300162306a36Sopenharmony_ciout: 300262306a36Sopenharmony_ci kfree(args); 300362306a36Sopenharmony_ci free_dentry_path(page); 300462306a36Sopenharmony_ci free_xid(xid); 300562306a36Sopenharmony_ci return rc; 300662306a36Sopenharmony_ci} 300762306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ 300862306a36Sopenharmony_ci 300962306a36Sopenharmony_cistatic int 301062306a36Sopenharmony_cicifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) 301162306a36Sopenharmony_ci{ 301262306a36Sopenharmony_ci unsigned int xid; 301362306a36Sopenharmony_ci kuid_t uid = INVALID_UID; 301462306a36Sopenharmony_ci kgid_t gid = INVALID_GID; 301562306a36Sopenharmony_ci struct inode *inode = d_inode(direntry); 301662306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); 301762306a36Sopenharmony_ci struct cifsInodeInfo *cifsInode = CIFS_I(inode); 301862306a36Sopenharmony_ci struct cifsFileInfo *wfile; 301962306a36Sopenharmony_ci struct cifs_tcon *tcon; 302062306a36Sopenharmony_ci const char *full_path; 302162306a36Sopenharmony_ci void *page = alloc_dentry_path(); 302262306a36Sopenharmony_ci int rc = -EACCES; 302362306a36Sopenharmony_ci __u32 dosattr = 0; 302462306a36Sopenharmony_ci __u64 mode = NO_CHANGE_64; 302562306a36Sopenharmony_ci 302662306a36Sopenharmony_ci xid = get_xid(); 302762306a36Sopenharmony_ci 302862306a36Sopenharmony_ci cifs_dbg(FYI, "setattr on file %pd attrs->ia_valid 0x%x\n", 302962306a36Sopenharmony_ci direntry, attrs->ia_valid); 303062306a36Sopenharmony_ci 303162306a36Sopenharmony_ci if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) 303262306a36Sopenharmony_ci attrs->ia_valid |= ATTR_FORCE; 303362306a36Sopenharmony_ci 303462306a36Sopenharmony_ci rc = setattr_prepare(&nop_mnt_idmap, direntry, attrs); 303562306a36Sopenharmony_ci if (rc < 0) 303662306a36Sopenharmony_ci goto cifs_setattr_exit; 303762306a36Sopenharmony_ci 303862306a36Sopenharmony_ci full_path = build_path_from_dentry(direntry, page); 303962306a36Sopenharmony_ci if (IS_ERR(full_path)) { 304062306a36Sopenharmony_ci rc = PTR_ERR(full_path); 304162306a36Sopenharmony_ci goto cifs_setattr_exit; 304262306a36Sopenharmony_ci } 304362306a36Sopenharmony_ci 304462306a36Sopenharmony_ci /* 304562306a36Sopenharmony_ci * Attempt to flush data before changing attributes. We need to do 304662306a36Sopenharmony_ci * this for ATTR_SIZE and ATTR_MTIME. If the flush of the data 304762306a36Sopenharmony_ci * returns error, store it to report later and continue. 304862306a36Sopenharmony_ci * 304962306a36Sopenharmony_ci * BB: This should be smarter. Why bother flushing pages that 305062306a36Sopenharmony_ci * will be truncated anyway? Also, should we error out here if 305162306a36Sopenharmony_ci * the flush returns error? Do we need to check for ATTR_MTIME_SET flag? 305262306a36Sopenharmony_ci */ 305362306a36Sopenharmony_ci if (attrs->ia_valid & (ATTR_MTIME | ATTR_SIZE | ATTR_CTIME)) { 305462306a36Sopenharmony_ci rc = filemap_write_and_wait(inode->i_mapping); 305562306a36Sopenharmony_ci if (is_interrupt_error(rc)) { 305662306a36Sopenharmony_ci rc = -ERESTARTSYS; 305762306a36Sopenharmony_ci goto cifs_setattr_exit; 305862306a36Sopenharmony_ci } 305962306a36Sopenharmony_ci mapping_set_error(inode->i_mapping, rc); 306062306a36Sopenharmony_ci } 306162306a36Sopenharmony_ci 306262306a36Sopenharmony_ci rc = 0; 306362306a36Sopenharmony_ci 306462306a36Sopenharmony_ci if ((attrs->ia_valid & ATTR_MTIME) && 306562306a36Sopenharmony_ci !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) { 306662306a36Sopenharmony_ci rc = cifs_get_writable_file(cifsInode, FIND_WR_ANY, &wfile); 306762306a36Sopenharmony_ci if (!rc) { 306862306a36Sopenharmony_ci tcon = tlink_tcon(wfile->tlink); 306962306a36Sopenharmony_ci rc = tcon->ses->server->ops->flush(xid, tcon, &wfile->fid); 307062306a36Sopenharmony_ci cifsFileInfo_put(wfile); 307162306a36Sopenharmony_ci if (rc) 307262306a36Sopenharmony_ci goto cifs_setattr_exit; 307362306a36Sopenharmony_ci } else if (rc != -EBADF) 307462306a36Sopenharmony_ci goto cifs_setattr_exit; 307562306a36Sopenharmony_ci else 307662306a36Sopenharmony_ci rc = 0; 307762306a36Sopenharmony_ci } 307862306a36Sopenharmony_ci 307962306a36Sopenharmony_ci if (attrs->ia_valid & ATTR_SIZE) { 308062306a36Sopenharmony_ci rc = cifs_set_file_size(inode, attrs, xid, full_path); 308162306a36Sopenharmony_ci if (rc != 0) 308262306a36Sopenharmony_ci goto cifs_setattr_exit; 308362306a36Sopenharmony_ci } 308462306a36Sopenharmony_ci 308562306a36Sopenharmony_ci if (attrs->ia_valid & ATTR_UID) 308662306a36Sopenharmony_ci uid = attrs->ia_uid; 308762306a36Sopenharmony_ci 308862306a36Sopenharmony_ci if (attrs->ia_valid & ATTR_GID) 308962306a36Sopenharmony_ci gid = attrs->ia_gid; 309062306a36Sopenharmony_ci 309162306a36Sopenharmony_ci if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) || 309262306a36Sopenharmony_ci (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID)) { 309362306a36Sopenharmony_ci if (uid_valid(uid) || gid_valid(gid)) { 309462306a36Sopenharmony_ci mode = NO_CHANGE_64; 309562306a36Sopenharmony_ci rc = id_mode_to_cifs_acl(inode, full_path, &mode, 309662306a36Sopenharmony_ci uid, gid); 309762306a36Sopenharmony_ci if (rc) { 309862306a36Sopenharmony_ci cifs_dbg(FYI, "%s: Setting id failed with error: %d\n", 309962306a36Sopenharmony_ci __func__, rc); 310062306a36Sopenharmony_ci goto cifs_setattr_exit; 310162306a36Sopenharmony_ci } 310262306a36Sopenharmony_ci } 310362306a36Sopenharmony_ci } else 310462306a36Sopenharmony_ci if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)) 310562306a36Sopenharmony_ci attrs->ia_valid &= ~(ATTR_UID | ATTR_GID); 310662306a36Sopenharmony_ci 310762306a36Sopenharmony_ci /* skip mode change if it's just for clearing setuid/setgid */ 310862306a36Sopenharmony_ci if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID)) 310962306a36Sopenharmony_ci attrs->ia_valid &= ~ATTR_MODE; 311062306a36Sopenharmony_ci 311162306a36Sopenharmony_ci if (attrs->ia_valid & ATTR_MODE) { 311262306a36Sopenharmony_ci mode = attrs->ia_mode; 311362306a36Sopenharmony_ci rc = 0; 311462306a36Sopenharmony_ci if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) || 311562306a36Sopenharmony_ci (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID)) { 311662306a36Sopenharmony_ci rc = id_mode_to_cifs_acl(inode, full_path, &mode, 311762306a36Sopenharmony_ci INVALID_UID, INVALID_GID); 311862306a36Sopenharmony_ci if (rc) { 311962306a36Sopenharmony_ci cifs_dbg(FYI, "%s: Setting ACL failed with error: %d\n", 312062306a36Sopenharmony_ci __func__, rc); 312162306a36Sopenharmony_ci goto cifs_setattr_exit; 312262306a36Sopenharmony_ci } 312362306a36Sopenharmony_ci 312462306a36Sopenharmony_ci /* 312562306a36Sopenharmony_ci * In case of CIFS_MOUNT_CIFS_ACL, we cannot support all modes. 312662306a36Sopenharmony_ci * Pick up the actual mode bits that were set. 312762306a36Sopenharmony_ci */ 312862306a36Sopenharmony_ci if (mode != attrs->ia_mode) 312962306a36Sopenharmony_ci attrs->ia_mode = mode; 313062306a36Sopenharmony_ci } else 313162306a36Sopenharmony_ci if (((mode & S_IWUGO) == 0) && 313262306a36Sopenharmony_ci (cifsInode->cifsAttrs & ATTR_READONLY) == 0) { 313362306a36Sopenharmony_ci 313462306a36Sopenharmony_ci dosattr = cifsInode->cifsAttrs | ATTR_READONLY; 313562306a36Sopenharmony_ci 313662306a36Sopenharmony_ci /* fix up mode if we're not using dynperm */ 313762306a36Sopenharmony_ci if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) == 0) 313862306a36Sopenharmony_ci attrs->ia_mode = inode->i_mode & ~S_IWUGO; 313962306a36Sopenharmony_ci } else if ((mode & S_IWUGO) && 314062306a36Sopenharmony_ci (cifsInode->cifsAttrs & ATTR_READONLY)) { 314162306a36Sopenharmony_ci 314262306a36Sopenharmony_ci dosattr = cifsInode->cifsAttrs & ~ATTR_READONLY; 314362306a36Sopenharmony_ci /* Attributes of 0 are ignored */ 314462306a36Sopenharmony_ci if (dosattr == 0) 314562306a36Sopenharmony_ci dosattr |= ATTR_NORMAL; 314662306a36Sopenharmony_ci 314762306a36Sopenharmony_ci /* reset local inode permissions to normal */ 314862306a36Sopenharmony_ci if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) { 314962306a36Sopenharmony_ci attrs->ia_mode &= ~(S_IALLUGO); 315062306a36Sopenharmony_ci if (S_ISDIR(inode->i_mode)) 315162306a36Sopenharmony_ci attrs->ia_mode |= 315262306a36Sopenharmony_ci cifs_sb->ctx->dir_mode; 315362306a36Sopenharmony_ci else 315462306a36Sopenharmony_ci attrs->ia_mode |= 315562306a36Sopenharmony_ci cifs_sb->ctx->file_mode; 315662306a36Sopenharmony_ci } 315762306a36Sopenharmony_ci } else if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) { 315862306a36Sopenharmony_ci /* ignore mode change - ATTR_READONLY hasn't changed */ 315962306a36Sopenharmony_ci attrs->ia_valid &= ~ATTR_MODE; 316062306a36Sopenharmony_ci } 316162306a36Sopenharmony_ci } 316262306a36Sopenharmony_ci 316362306a36Sopenharmony_ci if (attrs->ia_valid & (ATTR_MTIME|ATTR_ATIME|ATTR_CTIME) || 316462306a36Sopenharmony_ci ((attrs->ia_valid & ATTR_MODE) && dosattr)) { 316562306a36Sopenharmony_ci rc = cifs_set_file_info(inode, attrs, xid, full_path, dosattr); 316662306a36Sopenharmony_ci /* BB: check for rc = -EOPNOTSUPP and switch to legacy mode */ 316762306a36Sopenharmony_ci 316862306a36Sopenharmony_ci /* Even if error on time set, no sense failing the call if 316962306a36Sopenharmony_ci the server would set the time to a reasonable value anyway, 317062306a36Sopenharmony_ci and this check ensures that we are not being called from 317162306a36Sopenharmony_ci sys_utimes in which case we ought to fail the call back to 317262306a36Sopenharmony_ci the user when the server rejects the call */ 317362306a36Sopenharmony_ci if ((rc) && (attrs->ia_valid & 317462306a36Sopenharmony_ci (ATTR_MODE | ATTR_GID | ATTR_UID | ATTR_SIZE))) 317562306a36Sopenharmony_ci rc = 0; 317662306a36Sopenharmony_ci } 317762306a36Sopenharmony_ci 317862306a36Sopenharmony_ci /* do not need local check to inode_check_ok since the server does 317962306a36Sopenharmony_ci that */ 318062306a36Sopenharmony_ci if (rc) 318162306a36Sopenharmony_ci goto cifs_setattr_exit; 318262306a36Sopenharmony_ci 318362306a36Sopenharmony_ci if ((attrs->ia_valid & ATTR_SIZE) && 318462306a36Sopenharmony_ci attrs->ia_size != i_size_read(inode)) { 318562306a36Sopenharmony_ci truncate_setsize(inode, attrs->ia_size); 318662306a36Sopenharmony_ci fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size); 318762306a36Sopenharmony_ci } 318862306a36Sopenharmony_ci 318962306a36Sopenharmony_ci setattr_copy(&nop_mnt_idmap, inode, attrs); 319062306a36Sopenharmony_ci mark_inode_dirty(inode); 319162306a36Sopenharmony_ci 319262306a36Sopenharmony_cicifs_setattr_exit: 319362306a36Sopenharmony_ci free_xid(xid); 319462306a36Sopenharmony_ci free_dentry_path(page); 319562306a36Sopenharmony_ci return rc; 319662306a36Sopenharmony_ci} 319762306a36Sopenharmony_ci 319862306a36Sopenharmony_ciint 319962306a36Sopenharmony_cicifs_setattr(struct mnt_idmap *idmap, struct dentry *direntry, 320062306a36Sopenharmony_ci struct iattr *attrs) 320162306a36Sopenharmony_ci{ 320262306a36Sopenharmony_ci struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb); 320362306a36Sopenharmony_ci int rc, retries = 0; 320462306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY 320562306a36Sopenharmony_ci struct cifs_tcon *pTcon = cifs_sb_master_tcon(cifs_sb); 320662306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ 320762306a36Sopenharmony_ci 320862306a36Sopenharmony_ci if (unlikely(cifs_forced_shutdown(cifs_sb))) 320962306a36Sopenharmony_ci return -EIO; 321062306a36Sopenharmony_ci 321162306a36Sopenharmony_ci do { 321262306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY 321362306a36Sopenharmony_ci if (pTcon->unix_ext) 321462306a36Sopenharmony_ci rc = cifs_setattr_unix(direntry, attrs); 321562306a36Sopenharmony_ci else 321662306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */ 321762306a36Sopenharmony_ci rc = cifs_setattr_nounix(direntry, attrs); 321862306a36Sopenharmony_ci retries++; 321962306a36Sopenharmony_ci } while (is_retryable_error(rc) && retries < 2); 322062306a36Sopenharmony_ci 322162306a36Sopenharmony_ci /* BB: add cifs_setattr_legacy for really old servers */ 322262306a36Sopenharmony_ci return rc; 322362306a36Sopenharmony_ci} 3224