Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: LGPL-2.1
0002 /*
0003  *
0004  *   vfs operations that deal with io control
0005  *
0006  *   Copyright (C) International Business Machines  Corp., 2005,2013
0007  *   Author(s): Steve French (sfrench@us.ibm.com)
0008  *
0009  */
0010 
0011 #include <linux/fs.h>
0012 #include <linux/file.h>
0013 #include <linux/mount.h>
0014 #include <linux/mm.h>
0015 #include <linux/pagemap.h>
0016 #include "cifspdu.h"
0017 #include "cifsglob.h"
0018 #include "cifsproto.h"
0019 #include "cifs_debug.h"
0020 #include "cifsfs.h"
0021 #include "cifs_ioctl.h"
0022 #include "smb2proto.h"
0023 #include "smb2glob.h"
0024 #include <linux/btrfs.h>
0025 
0026 static long cifs_ioctl_query_info(unsigned int xid, struct file *filep,
0027                   unsigned long p)
0028 {
0029     struct inode *inode = file_inode(filep);
0030     struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
0031     struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
0032     struct dentry *dentry = filep->f_path.dentry;
0033     const unsigned char *path;
0034     void *page = alloc_dentry_path();
0035     __le16 *utf16_path = NULL, root_path;
0036     int rc = 0;
0037 
0038     path = build_path_from_dentry(dentry, page);
0039     if (IS_ERR(path)) {
0040         free_dentry_path(page);
0041         return PTR_ERR(path);
0042     }
0043 
0044     cifs_dbg(FYI, "%s %s\n", __func__, path);
0045 
0046     if (!path[0]) {
0047         root_path = 0;
0048         utf16_path = &root_path;
0049     } else {
0050         utf16_path = cifs_convert_path_to_utf16(path + 1, cifs_sb);
0051         if (!utf16_path) {
0052             rc = -ENOMEM;
0053             goto ici_exit;
0054         }
0055     }
0056 
0057     if (tcon->ses->server->ops->ioctl_query_info)
0058         rc = tcon->ses->server->ops->ioctl_query_info(
0059                 xid, tcon, cifs_sb, utf16_path,
0060                 filep->private_data ? 0 : 1, p);
0061     else
0062         rc = -EOPNOTSUPP;
0063 
0064  ici_exit:
0065     if (utf16_path != &root_path)
0066         kfree(utf16_path);
0067     free_dentry_path(page);
0068     return rc;
0069 }
0070 
0071 static long cifs_ioctl_copychunk(unsigned int xid, struct file *dst_file,
0072             unsigned long srcfd)
0073 {
0074     int rc;
0075     struct fd src_file;
0076     struct inode *src_inode;
0077 
0078     cifs_dbg(FYI, "ioctl copychunk range\n");
0079     /* the destination must be opened for writing */
0080     if (!(dst_file->f_mode & FMODE_WRITE)) {
0081         cifs_dbg(FYI, "file target not open for write\n");
0082         return -EINVAL;
0083     }
0084 
0085     /* check if target volume is readonly and take reference */
0086     rc = mnt_want_write_file(dst_file);
0087     if (rc) {
0088         cifs_dbg(FYI, "mnt_want_write failed with rc %d\n", rc);
0089         return rc;
0090     }
0091 
0092     src_file = fdget(srcfd);
0093     if (!src_file.file) {
0094         rc = -EBADF;
0095         goto out_drop_write;
0096     }
0097 
0098     if (src_file.file->f_op->unlocked_ioctl != cifs_ioctl) {
0099         rc = -EBADF;
0100         cifs_dbg(VFS, "src file seems to be from a different filesystem type\n");
0101         goto out_fput;
0102     }
0103 
0104     src_inode = file_inode(src_file.file);
0105     rc = -EINVAL;
0106     if (S_ISDIR(src_inode->i_mode))
0107         goto out_fput;
0108 
0109     rc = cifs_file_copychunk_range(xid, src_file.file, 0, dst_file, 0,
0110                     src_inode->i_size, 0);
0111     if (rc > 0)
0112         rc = 0;
0113 out_fput:
0114     fdput(src_file);
0115 out_drop_write:
0116     mnt_drop_write_file(dst_file);
0117     return rc;
0118 }
0119 
0120 static long smb_mnt_get_fsinfo(unsigned int xid, struct cifs_tcon *tcon,
0121                 void __user *arg)
0122 {
0123     int rc = 0;
0124     struct smb_mnt_fs_info *fsinf;
0125 
0126     fsinf = kzalloc(sizeof(struct smb_mnt_fs_info), GFP_KERNEL);
0127     if (fsinf == NULL)
0128         return -ENOMEM;
0129 
0130     fsinf->version = 1;
0131     fsinf->protocol_id = tcon->ses->server->vals->protocol_id;
0132     fsinf->device_characteristics =
0133             le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics);
0134     fsinf->device_type = le32_to_cpu(tcon->fsDevInfo.DeviceType);
0135     fsinf->fs_attributes = le32_to_cpu(tcon->fsAttrInfo.Attributes);
0136     fsinf->max_path_component =
0137         le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength);
0138     fsinf->vol_serial_number = tcon->vol_serial_number;
0139     fsinf->vol_create_time = le64_to_cpu(tcon->vol_create_time);
0140     fsinf->share_flags = tcon->share_flags;
0141     fsinf->share_caps = le32_to_cpu(tcon->capabilities);
0142     fsinf->sector_flags = tcon->ss_flags;
0143     fsinf->optimal_sector_size = tcon->perf_sector_size;
0144     fsinf->max_bytes_chunk = tcon->max_bytes_chunk;
0145     fsinf->maximal_access = tcon->maximal_access;
0146     fsinf->cifs_posix_caps = le64_to_cpu(tcon->fsUnixInfo.Capability);
0147 
0148     if (copy_to_user(arg, fsinf, sizeof(struct smb_mnt_fs_info)))
0149         rc = -EFAULT;
0150 
0151     kfree(fsinf);
0152     return rc;
0153 }
0154 
0155 static int cifs_shutdown(struct super_block *sb, unsigned long arg)
0156 {
0157     struct cifs_sb_info *sbi = CIFS_SB(sb);
0158     __u32 flags;
0159 
0160     if (!capable(CAP_SYS_ADMIN))
0161         return -EPERM;
0162 
0163     if (get_user(flags, (__u32 __user *)arg))
0164         return -EFAULT;
0165 
0166     if (flags > CIFS_GOING_FLAGS_NOLOGFLUSH)
0167         return -EINVAL;
0168 
0169     if (cifs_forced_shutdown(sbi))
0170         return 0;
0171 
0172     cifs_dbg(VFS, "shut down requested (%d)", flags);
0173 /*  trace_cifs_shutdown(sb, flags);*/
0174 
0175     /*
0176      * see:
0177      *   https://man7.org/linux/man-pages/man2/ioctl_xfs_goingdown.2.html
0178      * for more information and description of original intent of the flags
0179      */
0180     switch (flags) {
0181     /*
0182      * We could add support later for default flag which requires:
0183      *     "Flush all dirty data and metadata to disk"
0184      * would need to call syncfs or equivalent to flush page cache for
0185      * the mount and then issue fsync to server (if nostrictsync not set)
0186      */
0187     case CIFS_GOING_FLAGS_DEFAULT:
0188         cifs_dbg(FYI, "shutdown with default flag not supported\n");
0189         return -EINVAL;
0190     /*
0191      * FLAGS_LOGFLUSH is easy since it asks to write out metadata (not
0192      * data) but metadata writes are not cached on the client, so can treat
0193      * it similarly to NOLOGFLUSH
0194      */
0195     case CIFS_GOING_FLAGS_LOGFLUSH:
0196     case CIFS_GOING_FLAGS_NOLOGFLUSH:
0197         sbi->mnt_cifs_flags |= CIFS_MOUNT_SHUTDOWN;
0198         return 0;
0199     default:
0200         return -EINVAL;
0201     }
0202     return 0;
0203 }
0204 
0205 static int cifs_dump_full_key(struct cifs_tcon *tcon, struct smb3_full_key_debug_info __user *in)
0206 {
0207     struct smb3_full_key_debug_info out;
0208     struct cifs_ses *ses;
0209     int rc = 0;
0210     bool found = false;
0211     u8 __user *end;
0212 
0213     if (!smb3_encryption_required(tcon)) {
0214         rc = -EOPNOTSUPP;
0215         goto out;
0216     }
0217 
0218     /* copy user input into our output buffer */
0219     if (copy_from_user(&out, in, sizeof(out))) {
0220         rc = -EINVAL;
0221         goto out;
0222     }
0223 
0224     if (!out.session_id) {
0225         /* if ses id is 0, use current user session */
0226         ses = tcon->ses;
0227     } else {
0228         /* otherwise if a session id is given, look for it in all our sessions */
0229         struct cifs_ses *ses_it = NULL;
0230         struct TCP_Server_Info *server_it = NULL;
0231 
0232         spin_lock(&cifs_tcp_ses_lock);
0233         list_for_each_entry(server_it, &cifs_tcp_ses_list, tcp_ses_list) {
0234             list_for_each_entry(ses_it, &server_it->smb_ses_list, smb_ses_list) {
0235                 if (ses_it->Suid == out.session_id) {
0236                     ses = ses_it;
0237                     /*
0238                      * since we are using the session outside the crit
0239                      * section, we need to make sure it won't be released
0240                      * so increment its refcount
0241                      */
0242                     ses->ses_count++;
0243                     found = true;
0244                     goto search_end;
0245                 }
0246             }
0247         }
0248 search_end:
0249         spin_unlock(&cifs_tcp_ses_lock);
0250         if (!found) {
0251             rc = -ENOENT;
0252             goto out;
0253         }
0254     }
0255 
0256     switch (ses->server->cipher_type) {
0257     case SMB2_ENCRYPTION_AES128_CCM:
0258     case SMB2_ENCRYPTION_AES128_GCM:
0259         out.session_key_length = CIFS_SESS_KEY_SIZE;
0260         out.server_in_key_length = out.server_out_key_length = SMB3_GCM128_CRYPTKEY_SIZE;
0261         break;
0262     case SMB2_ENCRYPTION_AES256_CCM:
0263     case SMB2_ENCRYPTION_AES256_GCM:
0264         out.session_key_length = CIFS_SESS_KEY_SIZE;
0265         out.server_in_key_length = out.server_out_key_length = SMB3_GCM256_CRYPTKEY_SIZE;
0266         break;
0267     default:
0268         rc = -EOPNOTSUPP;
0269         goto out;
0270     }
0271 
0272     /* check if user buffer is big enough to store all the keys */
0273     if (out.in_size < sizeof(out) + out.session_key_length + out.server_in_key_length
0274         + out.server_out_key_length) {
0275         rc = -ENOBUFS;
0276         goto out;
0277     }
0278 
0279     out.session_id = ses->Suid;
0280     out.cipher_type = le16_to_cpu(ses->server->cipher_type);
0281 
0282     /* overwrite user input with our output */
0283     if (copy_to_user(in, &out, sizeof(out))) {
0284         rc = -EINVAL;
0285         goto out;
0286     }
0287 
0288     /* append all the keys at the end of the user buffer */
0289     end = in->data;
0290     if (copy_to_user(end, ses->auth_key.response, out.session_key_length)) {
0291         rc = -EINVAL;
0292         goto out;
0293     }
0294     end += out.session_key_length;
0295 
0296     if (copy_to_user(end, ses->smb3encryptionkey, out.server_in_key_length)) {
0297         rc = -EINVAL;
0298         goto out;
0299     }
0300     end += out.server_in_key_length;
0301 
0302     if (copy_to_user(end, ses->smb3decryptionkey, out.server_out_key_length)) {
0303         rc = -EINVAL;
0304         goto out;
0305     }
0306 
0307 out:
0308     if (found)
0309         cifs_put_smb_ses(ses);
0310     return rc;
0311 }
0312 
0313 long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
0314 {
0315     struct inode *inode = file_inode(filep);
0316     struct smb3_key_debug_info pkey_inf;
0317     int rc = -ENOTTY; /* strange error - but the precedent */
0318     unsigned int xid;
0319     struct cifsFileInfo *pSMBFile = filep->private_data;
0320     struct cifs_tcon *tcon;
0321     struct tcon_link *tlink;
0322     struct cifs_sb_info *cifs_sb;
0323     __u64   ExtAttrBits = 0;
0324     __u64   caps;
0325 
0326     xid = get_xid();
0327 
0328     cifs_dbg(FYI, "cifs ioctl 0x%x\n", command);
0329     switch (command) {
0330         case FS_IOC_GETFLAGS:
0331             if (pSMBFile == NULL)
0332                 break;
0333             tcon = tlink_tcon(pSMBFile->tlink);
0334             caps = le64_to_cpu(tcon->fsUnixInfo.Capability);
0335 #ifdef CONFIG_CIFS_POSIX
0336 #ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
0337             if (CIFS_UNIX_EXTATTR_CAP & caps) {
0338                 __u64   ExtAttrMask = 0;
0339                 rc = CIFSGetExtAttr(xid, tcon,
0340                             pSMBFile->fid.netfid,
0341                             &ExtAttrBits, &ExtAttrMask);
0342                 if (rc == 0)
0343                     rc = put_user(ExtAttrBits &
0344                         FS_FL_USER_VISIBLE,
0345                         (int __user *)arg);
0346                 if (rc != EOPNOTSUPP)
0347                     break;
0348             }
0349 #endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
0350 #endif /* CONFIG_CIFS_POSIX */
0351             rc = 0;
0352             if (CIFS_I(inode)->cifsAttrs & ATTR_COMPRESSED) {
0353                 /* add in the compressed bit */
0354                 ExtAttrBits = FS_COMPR_FL;
0355                 rc = put_user(ExtAttrBits & FS_FL_USER_VISIBLE,
0356                           (int __user *)arg);
0357             }
0358             break;
0359         case FS_IOC_SETFLAGS:
0360             if (pSMBFile == NULL)
0361                 break;
0362             tcon = tlink_tcon(pSMBFile->tlink);
0363             /* caps = le64_to_cpu(tcon->fsUnixInfo.Capability); */
0364 
0365             if (get_user(ExtAttrBits, (int __user *)arg)) {
0366                 rc = -EFAULT;
0367                 break;
0368             }
0369 
0370             /*
0371              * if (CIFS_UNIX_EXTATTR_CAP & caps)
0372              *  rc = CIFSSetExtAttr(xid, tcon,
0373              *             pSMBFile->fid.netfid,
0374              *             extAttrBits,
0375              *             &ExtAttrMask);
0376              * if (rc != EOPNOTSUPP)
0377              *  break;
0378              */
0379 
0380             /* Currently only flag we can set is compressed flag */
0381             if ((ExtAttrBits & FS_COMPR_FL) == 0)
0382                 break;
0383 
0384             /* Try to set compress flag */
0385             if (tcon->ses->server->ops->set_compression) {
0386                 rc = tcon->ses->server->ops->set_compression(
0387                             xid, tcon, pSMBFile);
0388                 cifs_dbg(FYI, "set compress flag rc %d\n", rc);
0389             }
0390             break;
0391         case CIFS_IOC_COPYCHUNK_FILE:
0392             rc = cifs_ioctl_copychunk(xid, filep, arg);
0393             break;
0394         case CIFS_QUERY_INFO:
0395             rc = cifs_ioctl_query_info(xid, filep, arg);
0396             break;
0397         case CIFS_IOC_SET_INTEGRITY:
0398             if (pSMBFile == NULL)
0399                 break;
0400             tcon = tlink_tcon(pSMBFile->tlink);
0401             if (tcon->ses->server->ops->set_integrity)
0402                 rc = tcon->ses->server->ops->set_integrity(xid,
0403                         tcon, pSMBFile);
0404             else
0405                 rc = -EOPNOTSUPP;
0406             break;
0407         case CIFS_IOC_GET_MNT_INFO:
0408             if (pSMBFile == NULL)
0409                 break;
0410             tcon = tlink_tcon(pSMBFile->tlink);
0411             rc = smb_mnt_get_fsinfo(xid, tcon, (void __user *)arg);
0412             break;
0413         case CIFS_ENUMERATE_SNAPSHOTS:
0414             if (pSMBFile == NULL)
0415                 break;
0416             if (arg == 0) {
0417                 rc = -EINVAL;
0418                 goto cifs_ioc_exit;
0419             }
0420             tcon = tlink_tcon(pSMBFile->tlink);
0421             if (tcon->ses->server->ops->enum_snapshots)
0422                 rc = tcon->ses->server->ops->enum_snapshots(xid, tcon,
0423                         pSMBFile, (void __user *)arg);
0424             else
0425                 rc = -EOPNOTSUPP;
0426             break;
0427         case CIFS_DUMP_KEY:
0428             /*
0429              * Dump encryption keys. This is an old ioctl that only
0430              * handles AES-128-{CCM,GCM}.
0431              */
0432             if (pSMBFile == NULL)
0433                 break;
0434             if (!capable(CAP_SYS_ADMIN)) {
0435                 rc = -EACCES;
0436                 break;
0437             }
0438 
0439             tcon = tlink_tcon(pSMBFile->tlink);
0440             if (!smb3_encryption_required(tcon)) {
0441                 rc = -EOPNOTSUPP;
0442                 break;
0443             }
0444             pkey_inf.cipher_type =
0445                 le16_to_cpu(tcon->ses->server->cipher_type);
0446             pkey_inf.Suid = tcon->ses->Suid;
0447             memcpy(pkey_inf.auth_key, tcon->ses->auth_key.response,
0448                     16 /* SMB2_NTLMV2_SESSKEY_SIZE */);
0449             memcpy(pkey_inf.smb3decryptionkey,
0450                   tcon->ses->smb3decryptionkey, SMB3_SIGN_KEY_SIZE);
0451             memcpy(pkey_inf.smb3encryptionkey,
0452                   tcon->ses->smb3encryptionkey, SMB3_SIGN_KEY_SIZE);
0453             if (copy_to_user((void __user *)arg, &pkey_inf,
0454                     sizeof(struct smb3_key_debug_info)))
0455                 rc = -EFAULT;
0456             else
0457                 rc = 0;
0458             break;
0459         case CIFS_DUMP_FULL_KEY:
0460             /*
0461              * Dump encryption keys (handles any key sizes)
0462              */
0463             if (pSMBFile == NULL)
0464                 break;
0465             if (!capable(CAP_SYS_ADMIN)) {
0466                 rc = -EACCES;
0467                 break;
0468             }
0469             tcon = tlink_tcon(pSMBFile->tlink);
0470             rc = cifs_dump_full_key(tcon, (void __user *)arg);
0471             break;
0472         case CIFS_IOC_NOTIFY:
0473             if (!S_ISDIR(inode->i_mode)) {
0474                 /* Notify can only be done on directories */
0475                 rc = -EOPNOTSUPP;
0476                 break;
0477             }
0478             cifs_sb = CIFS_SB(inode->i_sb);
0479             tlink = cifs_sb_tlink(cifs_sb);
0480             if (IS_ERR(tlink)) {
0481                 rc = PTR_ERR(tlink);
0482                 break;
0483             }
0484             tcon = tlink_tcon(tlink);
0485             if (tcon && tcon->ses->server->ops->notify) {
0486                 rc = tcon->ses->server->ops->notify(xid,
0487                         filep, (void __user *)arg);
0488                 cifs_dbg(FYI, "ioctl notify rc %d\n", rc);
0489             } else
0490                 rc = -EOPNOTSUPP;
0491             cifs_put_tlink(tlink);
0492             break;
0493         case CIFS_IOC_SHUTDOWN:
0494             rc = cifs_shutdown(inode->i_sb, arg);
0495             break;
0496         default:
0497             cifs_dbg(FYI, "unsupported ioctl\n");
0498             break;
0499     }
0500 cifs_ioc_exit:
0501     free_xid(xid);
0502     return rc;
0503 }