Merge tag 'tty-5.13-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
[linux-2.6-microblaze.git] / fs / cifs / ioctl.c
1 /*
2  *   fs/cifs/ioctl.c
3  *
4  *   vfs operations that deal with io control
5  *
6  *   Copyright (C) International Business Machines  Corp., 2005,2013
7  *   Author(s): Steve French (sfrench@us.ibm.com)
8  *
9  *   This library is free software; you can redistribute it and/or modify
10  *   it under the terms of the GNU Lesser General Public License as published
11  *   by the Free Software Foundation; either version 2.1 of the License, or
12  *   (at your option) any later version.
13  *
14  *   This library is distributed in the hope that it will be useful,
15  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
17  *   the GNU Lesser General Public License for more details.
18  *
19  *   You should have received a copy of the GNU Lesser General Public License
20  *   along with this library; if not, write to the Free Software
21  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22  */
23
24 #include <linux/fs.h>
25 #include <linux/file.h>
26 #include <linux/mount.h>
27 #include <linux/mm.h>
28 #include <linux/pagemap.h>
29 #include "cifspdu.h"
30 #include "cifsglob.h"
31 #include "cifsproto.h"
32 #include "cifs_debug.h"
33 #include "cifsfs.h"
34 #include "cifs_ioctl.h"
35 #include "smb2proto.h"
36 #include "smb2glob.h"
37 #include <linux/btrfs.h>
38
39 static long cifs_ioctl_query_info(unsigned int xid, struct file *filep,
40                                   unsigned long p)
41 {
42         struct inode *inode = file_inode(filep);
43         struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
44         struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
45         struct dentry *dentry = filep->f_path.dentry;
46         const unsigned char *path;
47         void *page = alloc_dentry_path();
48         __le16 *utf16_path = NULL, root_path;
49         int rc = 0;
50
51         path = build_path_from_dentry(dentry, page);
52         if (IS_ERR(path)) {
53                 free_dentry_path(page);
54                 return PTR_ERR(path);
55         }
56
57         cifs_dbg(FYI, "%s %s\n", __func__, path);
58
59         if (!path[0]) {
60                 root_path = 0;
61                 utf16_path = &root_path;
62         } else {
63                 utf16_path = cifs_convert_path_to_utf16(path + 1, cifs_sb);
64                 if (!utf16_path) {
65                         rc = -ENOMEM;
66                         goto ici_exit;
67                 }
68         }
69
70         if (tcon->ses->server->ops->ioctl_query_info)
71                 rc = tcon->ses->server->ops->ioctl_query_info(
72                                 xid, tcon, cifs_sb, utf16_path,
73                                 filep->private_data ? 0 : 1, p);
74         else
75                 rc = -EOPNOTSUPP;
76
77  ici_exit:
78         if (utf16_path != &root_path)
79                 kfree(utf16_path);
80         free_dentry_path(page);
81         return rc;
82 }
83
84 static long cifs_ioctl_copychunk(unsigned int xid, struct file *dst_file,
85                         unsigned long srcfd)
86 {
87         int rc;
88         struct fd src_file;
89         struct inode *src_inode;
90
91         cifs_dbg(FYI, "ioctl copychunk range\n");
92         /* the destination must be opened for writing */
93         if (!(dst_file->f_mode & FMODE_WRITE)) {
94                 cifs_dbg(FYI, "file target not open for write\n");
95                 return -EINVAL;
96         }
97
98         /* check if target volume is readonly and take reference */
99         rc = mnt_want_write_file(dst_file);
100         if (rc) {
101                 cifs_dbg(FYI, "mnt_want_write failed with rc %d\n", rc);
102                 return rc;
103         }
104
105         src_file = fdget(srcfd);
106         if (!src_file.file) {
107                 rc = -EBADF;
108                 goto out_drop_write;
109         }
110
111         if (src_file.file->f_op->unlocked_ioctl != cifs_ioctl) {
112                 rc = -EBADF;
113                 cifs_dbg(VFS, "src file seems to be from a different filesystem type\n");
114                 goto out_fput;
115         }
116
117         src_inode = file_inode(src_file.file);
118         rc = -EINVAL;
119         if (S_ISDIR(src_inode->i_mode))
120                 goto out_fput;
121
122         rc = cifs_file_copychunk_range(xid, src_file.file, 0, dst_file, 0,
123                                         src_inode->i_size, 0);
124         if (rc > 0)
125                 rc = 0;
126 out_fput:
127         fdput(src_file);
128 out_drop_write:
129         mnt_drop_write_file(dst_file);
130         return rc;
131 }
132
133 static long smb_mnt_get_fsinfo(unsigned int xid, struct cifs_tcon *tcon,
134                                 void __user *arg)
135 {
136         int rc = 0;
137         struct smb_mnt_fs_info *fsinf;
138
139         fsinf = kzalloc(sizeof(struct smb_mnt_fs_info), GFP_KERNEL);
140         if (fsinf == NULL)
141                 return -ENOMEM;
142
143         fsinf->version = 1;
144         fsinf->protocol_id = tcon->ses->server->vals->protocol_id;
145         fsinf->device_characteristics =
146                         le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics);
147         fsinf->device_type = le32_to_cpu(tcon->fsDevInfo.DeviceType);
148         fsinf->fs_attributes = le32_to_cpu(tcon->fsAttrInfo.Attributes);
149         fsinf->max_path_component =
150                 le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength);
151         fsinf->vol_serial_number = tcon->vol_serial_number;
152         fsinf->vol_create_time = le64_to_cpu(tcon->vol_create_time);
153         fsinf->share_flags = tcon->share_flags;
154         fsinf->share_caps = le32_to_cpu(tcon->capabilities);
155         fsinf->sector_flags = tcon->ss_flags;
156         fsinf->optimal_sector_size = tcon->perf_sector_size;
157         fsinf->max_bytes_chunk = tcon->max_bytes_chunk;
158         fsinf->maximal_access = tcon->maximal_access;
159         fsinf->cifs_posix_caps = le64_to_cpu(tcon->fsUnixInfo.Capability);
160
161         if (copy_to_user(arg, fsinf, sizeof(struct smb_mnt_fs_info)))
162                 rc = -EFAULT;
163
164         kfree(fsinf);
165         return rc;
166 }
167
168 static int cifs_shutdown(struct super_block *sb, unsigned long arg)
169 {
170         struct cifs_sb_info *sbi = CIFS_SB(sb);
171         __u32 flags;
172
173         if (!capable(CAP_SYS_ADMIN))
174                 return -EPERM;
175
176         if (get_user(flags, (__u32 __user *)arg))
177                 return -EFAULT;
178
179         if (flags > CIFS_GOING_FLAGS_NOLOGFLUSH)
180                 return -EINVAL;
181
182         if (cifs_forced_shutdown(sbi))
183                 return 0;
184
185         cifs_dbg(VFS, "shut down requested (%d)", flags);
186 /*      trace_cifs_shutdown(sb, flags);*/
187
188         /*
189          * see:
190          *   https://man7.org/linux/man-pages/man2/ioctl_xfs_goingdown.2.html
191          * for more information and description of original intent of the flags
192          */
193         switch (flags) {
194         /*
195          * We could add support later for default flag which requires:
196          *     "Flush all dirty data and metadata to disk"
197          * would need to call syncfs or equivalent to flush page cache for
198          * the mount and then issue fsync to server (if nostrictsync not set)
199          */
200         case CIFS_GOING_FLAGS_DEFAULT:
201                 cifs_dbg(FYI, "shutdown with default flag not supported\n");
202                 return -EINVAL;
203         /*
204          * FLAGS_LOGFLUSH is easy since it asks to write out metadata (not
205          * data) but metadata writes are not cached on the client, so can treat
206          * it similarly to NOLOGFLUSH
207          */
208         case CIFS_GOING_FLAGS_LOGFLUSH:
209         case CIFS_GOING_FLAGS_NOLOGFLUSH:
210                 sbi->mnt_cifs_flags |= CIFS_MOUNT_SHUTDOWN;
211                 return 0;
212         default:
213                 return -EINVAL;
214         }
215         return 0;
216 }
217
218 static int cifs_dump_full_key(struct cifs_tcon *tcon, struct smb3_full_key_debug_info __user *in)
219 {
220         struct smb3_full_key_debug_info out;
221         struct cifs_ses *ses;
222         int rc = 0;
223         bool found = false;
224         u8 __user *end;
225
226         if (!smb3_encryption_required(tcon)) {
227                 rc = -EOPNOTSUPP;
228                 goto out;
229         }
230
231         /* copy user input into our output buffer */
232         if (copy_from_user(&out, in, sizeof(out))) {
233                 rc = -EINVAL;
234                 goto out;
235         }
236
237         if (!out.session_id) {
238                 /* if ses id is 0, use current user session */
239                 ses = tcon->ses;
240         } else {
241                 /* otherwise if a session id is given, look for it in all our sessions */
242                 struct cifs_ses *ses_it = NULL;
243                 struct TCP_Server_Info *server_it = NULL;
244
245                 spin_lock(&cifs_tcp_ses_lock);
246                 list_for_each_entry(server_it, &cifs_tcp_ses_list, tcp_ses_list) {
247                         list_for_each_entry(ses_it, &server_it->smb_ses_list, smb_ses_list) {
248                                 if (ses_it->Suid == out.session_id) {
249                                         ses = ses_it;
250                                         /*
251                                          * since we are using the session outside the crit
252                                          * section, we need to make sure it won't be released
253                                          * so increment its refcount
254                                          */
255                                         ses->ses_count++;
256                                         found = true;
257                                         goto search_end;
258                                 }
259                         }
260                 }
261 search_end:
262                 spin_unlock(&cifs_tcp_ses_lock);
263                 if (!found) {
264                         rc = -ENOENT;
265                         goto out;
266                 }
267         }
268
269         switch (ses->server->cipher_type) {
270         case SMB2_ENCRYPTION_AES128_CCM:
271         case SMB2_ENCRYPTION_AES128_GCM:
272                 out.session_key_length = CIFS_SESS_KEY_SIZE;
273                 out.server_in_key_length = out.server_out_key_length = SMB3_GCM128_CRYPTKEY_SIZE;
274                 break;
275         case SMB2_ENCRYPTION_AES256_CCM:
276         case SMB2_ENCRYPTION_AES256_GCM:
277                 out.session_key_length = CIFS_SESS_KEY_SIZE;
278                 out.server_in_key_length = out.server_out_key_length = SMB3_GCM256_CRYPTKEY_SIZE;
279                 break;
280         default:
281                 rc = -EOPNOTSUPP;
282                 goto out;
283         }
284
285         /* check if user buffer is big enough to store all the keys */
286         if (out.in_size < sizeof(out) + out.session_key_length + out.server_in_key_length
287             + out.server_out_key_length) {
288                 rc = -ENOBUFS;
289                 goto out;
290         }
291
292         out.session_id = ses->Suid;
293         out.cipher_type = le16_to_cpu(ses->server->cipher_type);
294
295         /* overwrite user input with our output */
296         if (copy_to_user(in, &out, sizeof(out))) {
297                 rc = -EINVAL;
298                 goto out;
299         }
300
301         /* append all the keys at the end of the user buffer */
302         end = in->data;
303         if (copy_to_user(end, ses->auth_key.response, out.session_key_length)) {
304                 rc = -EINVAL;
305                 goto out;
306         }
307         end += out.session_key_length;
308
309         if (copy_to_user(end, ses->smb3encryptionkey, out.server_in_key_length)) {
310                 rc = -EINVAL;
311                 goto out;
312         }
313         end += out.server_in_key_length;
314
315         if (copy_to_user(end, ses->smb3decryptionkey, out.server_out_key_length)) {
316                 rc = -EINVAL;
317                 goto out;
318         }
319
320 out:
321         if (found)
322                 cifs_put_smb_ses(ses);
323         return rc;
324 }
325
326 long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
327 {
328         struct inode *inode = file_inode(filep);
329         struct smb3_key_debug_info pkey_inf;
330         int rc = -ENOTTY; /* strange error - but the precedent */
331         unsigned int xid;
332         struct cifsFileInfo *pSMBFile = filep->private_data;
333         struct cifs_tcon *tcon;
334         struct tcon_link *tlink;
335         struct cifs_sb_info *cifs_sb;
336         __u64   ExtAttrBits = 0;
337         __u64   caps;
338
339         xid = get_xid();
340
341         cifs_dbg(FYI, "cifs ioctl 0x%x\n", command);
342         switch (command) {
343                 case FS_IOC_GETFLAGS:
344                         if (pSMBFile == NULL)
345                                 break;
346                         tcon = tlink_tcon(pSMBFile->tlink);
347                         caps = le64_to_cpu(tcon->fsUnixInfo.Capability);
348 #ifdef CONFIG_CIFS_POSIX
349                         if (CIFS_UNIX_EXTATTR_CAP & caps) {
350                                 __u64   ExtAttrMask = 0;
351                                 rc = CIFSGetExtAttr(xid, tcon,
352                                                     pSMBFile->fid.netfid,
353                                                     &ExtAttrBits, &ExtAttrMask);
354                                 if (rc == 0)
355                                         rc = put_user(ExtAttrBits &
356                                                 FS_FL_USER_VISIBLE,
357                                                 (int __user *)arg);
358                                 if (rc != EOPNOTSUPP)
359                                         break;
360                         }
361 #endif /* CONFIG_CIFS_POSIX */
362                         rc = 0;
363                         if (CIFS_I(inode)->cifsAttrs & ATTR_COMPRESSED) {
364                                 /* add in the compressed bit */
365                                 ExtAttrBits = FS_COMPR_FL;
366                                 rc = put_user(ExtAttrBits & FS_FL_USER_VISIBLE,
367                                               (int __user *)arg);
368                         }
369                         break;
370                 case FS_IOC_SETFLAGS:
371                         if (pSMBFile == NULL)
372                                 break;
373                         tcon = tlink_tcon(pSMBFile->tlink);
374                         caps = le64_to_cpu(tcon->fsUnixInfo.Capability);
375
376                         if (get_user(ExtAttrBits, (int __user *)arg)) {
377                                 rc = -EFAULT;
378                                 break;
379                         }
380
381                         /*
382                          * if (CIFS_UNIX_EXTATTR_CAP & caps)
383                          *      rc = CIFSSetExtAttr(xid, tcon,
384                          *                     pSMBFile->fid.netfid,
385                          *                     extAttrBits,
386                          *                     &ExtAttrMask);
387                          * if (rc != EOPNOTSUPP)
388                          *      break;
389                          */
390
391                         /* Currently only flag we can set is compressed flag */
392                         if ((ExtAttrBits & FS_COMPR_FL) == 0)
393                                 break;
394
395                         /* Try to set compress flag */
396                         if (tcon->ses->server->ops->set_compression) {
397                                 rc = tcon->ses->server->ops->set_compression(
398                                                         xid, tcon, pSMBFile);
399                                 cifs_dbg(FYI, "set compress flag rc %d\n", rc);
400                         }
401                         break;
402                 case CIFS_IOC_COPYCHUNK_FILE:
403                         rc = cifs_ioctl_copychunk(xid, filep, arg);
404                         break;
405                 case CIFS_QUERY_INFO:
406                         rc = cifs_ioctl_query_info(xid, filep, arg);
407                         break;
408                 case CIFS_IOC_SET_INTEGRITY:
409                         if (pSMBFile == NULL)
410                                 break;
411                         tcon = tlink_tcon(pSMBFile->tlink);
412                         if (tcon->ses->server->ops->set_integrity)
413                                 rc = tcon->ses->server->ops->set_integrity(xid,
414                                                 tcon, pSMBFile);
415                         else
416                                 rc = -EOPNOTSUPP;
417                         break;
418                 case CIFS_IOC_GET_MNT_INFO:
419                         if (pSMBFile == NULL)
420                                 break;
421                         tcon = tlink_tcon(pSMBFile->tlink);
422                         rc = smb_mnt_get_fsinfo(xid, tcon, (void __user *)arg);
423                         break;
424                 case CIFS_ENUMERATE_SNAPSHOTS:
425                         if (pSMBFile == NULL)
426                                 break;
427                         if (arg == 0) {
428                                 rc = -EINVAL;
429                                 goto cifs_ioc_exit;
430                         }
431                         tcon = tlink_tcon(pSMBFile->tlink);
432                         if (tcon->ses->server->ops->enum_snapshots)
433                                 rc = tcon->ses->server->ops->enum_snapshots(xid, tcon,
434                                                 pSMBFile, (void __user *)arg);
435                         else
436                                 rc = -EOPNOTSUPP;
437                         break;
438                 case CIFS_DUMP_KEY:
439                         /*
440                          * Dump encryption keys. This is an old ioctl that only
441                          * handles AES-128-{CCM,GCM}.
442                          */
443                         if (pSMBFile == NULL)
444                                 break;
445                         if (!capable(CAP_SYS_ADMIN)) {
446                                 rc = -EACCES;
447                                 break;
448                         }
449
450                         tcon = tlink_tcon(pSMBFile->tlink);
451                         if (!smb3_encryption_required(tcon)) {
452                                 rc = -EOPNOTSUPP;
453                                 break;
454                         }
455                         pkey_inf.cipher_type =
456                                 le16_to_cpu(tcon->ses->server->cipher_type);
457                         pkey_inf.Suid = tcon->ses->Suid;
458                         memcpy(pkey_inf.auth_key, tcon->ses->auth_key.response,
459                                         16 /* SMB2_NTLMV2_SESSKEY_SIZE */);
460                         memcpy(pkey_inf.smb3decryptionkey,
461                               tcon->ses->smb3decryptionkey, SMB3_SIGN_KEY_SIZE);
462                         memcpy(pkey_inf.smb3encryptionkey,
463                               tcon->ses->smb3encryptionkey, SMB3_SIGN_KEY_SIZE);
464                         if (copy_to_user((void __user *)arg, &pkey_inf,
465                                         sizeof(struct smb3_key_debug_info)))
466                                 rc = -EFAULT;
467                         else
468                                 rc = 0;
469                         break;
470                 case CIFS_DUMP_FULL_KEY:
471                         /*
472                          * Dump encryption keys (handles any key sizes)
473                          */
474                         if (pSMBFile == NULL)
475                                 break;
476                         if (!capable(CAP_SYS_ADMIN)) {
477                                 rc = -EACCES;
478                                 break;
479                         }
480                         tcon = tlink_tcon(pSMBFile->tlink);
481                         rc = cifs_dump_full_key(tcon, (void __user *)arg);
482                         break;
483                 case CIFS_IOC_NOTIFY:
484                         if (!S_ISDIR(inode->i_mode)) {
485                                 /* Notify can only be done on directories */
486                                 rc = -EOPNOTSUPP;
487                                 break;
488                         }
489                         cifs_sb = CIFS_SB(inode->i_sb);
490                         tlink = cifs_sb_tlink(cifs_sb);
491                         if (IS_ERR(tlink)) {
492                                 rc = PTR_ERR(tlink);
493                                 break;
494                         }
495                         tcon = tlink_tcon(tlink);
496                         if (tcon && tcon->ses->server->ops->notify) {
497                                 rc = tcon->ses->server->ops->notify(xid,
498                                                 filep, (void __user *)arg);
499                                 cifs_dbg(FYI, "ioctl notify rc %d\n", rc);
500                         } else
501                                 rc = -EOPNOTSUPP;
502                         cifs_put_tlink(tlink);
503                         break;
504                 case CIFS_IOC_SHUTDOWN:
505                         rc = cifs_shutdown(inode->i_sb, arg);
506                         break;
507                 default:
508                         cifs_dbg(FYI, "unsupported ioctl\n");
509                         break;
510         }
511 cifs_ioc_exit:
512         free_xid(xid);
513         return rc;
514 }