aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/cifs/cifsglob.h8
-rw-r--r--fs/cifs/dir.c107
-rw-r--r--fs/cifs/smb1ops.c126
-rw-r--r--fs/cifs/smb2ops.c102
4 files changed, 239 insertions, 104 deletions
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index c4f0f4e4bc6d..38feae812b47 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -479,6 +479,14 @@ struct smb_version_operations {
479 struct cifs_tcon *tcon, 479 struct cifs_tcon *tcon,
480 __le16 *path, int is_dir, 480 __le16 *path, int is_dir,
481 unsigned long p); 481 unsigned long p);
482 /* make unix special files (block, char, fifo, socket) */
483 int (*make_node)(unsigned int xid,
484 struct inode *inode,
485 struct dentry *dentry,
486 struct cifs_tcon *tcon,
487 char *full_path,
488 umode_t mode,
489 dev_t device_number);
482}; 490};
483 491
484struct smb_version_values { 492struct smb_version_values {
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index 907e85d65bb4..f26a48dd2e39 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -621,20 +621,10 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, umode_t mode,
621{ 621{
622 int rc = -EPERM; 622 int rc = -EPERM;
623 unsigned int xid; 623 unsigned int xid;
624 int create_options = CREATE_NOT_DIR | CREATE_OPTION_SPECIAL;
625 struct cifs_sb_info *cifs_sb; 624 struct cifs_sb_info *cifs_sb;
626 struct tcon_link *tlink; 625 struct tcon_link *tlink;
627 struct cifs_tcon *tcon; 626 struct cifs_tcon *tcon;
628 struct cifs_io_parms io_parms;
629 char *full_path = NULL; 627 char *full_path = NULL;
630 struct inode *newinode = NULL;
631 __u32 oplock = 0;
632 struct cifs_fid fid;
633 struct cifs_open_parms oparms;
634 FILE_ALL_INFO *buf = NULL;
635 unsigned int bytes_written;
636 struct win_dev *pdev;
637 struct kvec iov[2];
638 628
639 if (!old_valid_dev(device_number)) 629 if (!old_valid_dev(device_number))
640 return -EINVAL; 630 return -EINVAL;
@@ -654,103 +644,12 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, umode_t mode,
654 goto mknod_out; 644 goto mknod_out;
655 } 645 }
656 646
657 if (tcon->unix_ext) { 647 rc = tcon->ses->server->ops->make_node(xid, inode, direntry, tcon,
658 struct cifs_unix_set_info_args args = { 648 full_path, mode,
659 .mode = mode & ~current_umask(), 649 device_number);
660 .ctime = NO_CHANGE_64,
661 .atime = NO_CHANGE_64,
662 .mtime = NO_CHANGE_64,
663 .device = device_number,
664 };
665 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
666 args.uid = current_fsuid();
667 args.gid = current_fsgid();
668 } else {
669 args.uid = INVALID_UID; /* no change */
670 args.gid = INVALID_GID; /* no change */
671 }
672 rc = CIFSSMBUnixSetPathInfo(xid, tcon, full_path, &args,
673 cifs_sb->local_nls,
674 cifs_remap(cifs_sb));
675 if (rc)
676 goto mknod_out;
677
678 rc = cifs_get_inode_info_unix(&newinode, full_path,
679 inode->i_sb, xid);
680
681 if (rc == 0)
682 d_instantiate(direntry, newinode);
683 goto mknod_out;
684 }
685
686 if (!S_ISCHR(mode) && !S_ISBLK(mode))
687 goto mknod_out;
688
689 if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL))
690 goto mknod_out;
691
692
693 cifs_dbg(FYI, "sfu compat create special file\n");
694
695 buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
696 if (buf == NULL) {
697 rc = -ENOMEM;
698 goto mknod_out;
699 }
700
701 if (backup_cred(cifs_sb))
702 create_options |= CREATE_OPEN_BACKUP_INTENT;
703
704 oparms.tcon = tcon;
705 oparms.cifs_sb = cifs_sb;
706 oparms.desired_access = GENERIC_WRITE;
707 oparms.create_options = create_options;
708 oparms.disposition = FILE_CREATE;
709 oparms.path = full_path;
710 oparms.fid = &fid;
711 oparms.reconnect = false;
712
713 if (tcon->ses->server->oplocks)
714 oplock = REQ_OPLOCK;
715 else
716 oplock = 0;
717 rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, buf);
718 if (rc)
719 goto mknod_out;
720
721 /*
722 * BB Do not bother to decode buf since no local inode yet to put
723 * timestamps in, but we can reuse it safely.
724 */
725
726 pdev = (struct win_dev *)buf;
727 io_parms.pid = current->tgid;
728 io_parms.tcon = tcon;
729 io_parms.offset = 0;
730 io_parms.length = sizeof(struct win_dev);
731 iov[1].iov_base = buf;
732 iov[1].iov_len = sizeof(struct win_dev);
733 if (S_ISCHR(mode)) {
734 memcpy(pdev->type, "IntxCHR", 8);
735 pdev->major = cpu_to_le64(MAJOR(device_number));
736 pdev->minor = cpu_to_le64(MINOR(device_number));
737 rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms,
738 &bytes_written, iov, 1);
739 } else if (S_ISBLK(mode)) {
740 memcpy(pdev->type, "IntxBLK", 8);
741 pdev->major = cpu_to_le64(MAJOR(device_number));
742 pdev->minor = cpu_to_le64(MINOR(device_number));
743 rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms,
744 &bytes_written, iov, 1);
745 }
746 tcon->ses->server->ops->close(xid, tcon, &fid);
747 d_drop(direntry);
748
749 /* FIXME: add code here to set EAs */
750 650
751mknod_out: 651mknod_out:
752 kfree(full_path); 652 kfree(full_path);
753 kfree(buf);
754 free_xid(xid); 653 free_xid(xid);
755 cifs_put_tlink(tlink); 654 cifs_put_tlink(tlink);
756 return rc; 655 return rc;
diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c
index f0ce27c3c6e4..c711f1f39bf2 100644
--- a/fs/cifs/smb1ops.c
+++ b/fs/cifs/smb1ops.c
@@ -1027,6 +1027,131 @@ cifs_can_echo(struct TCP_Server_Info *server)
1027 return false; 1027 return false;
1028} 1028}
1029 1029
1030static int
1031cifs_make_node(unsigned int xid, struct inode *inode,
1032 struct dentry *dentry, struct cifs_tcon *tcon,
1033 char *full_path, umode_t mode, dev_t dev)
1034{
1035 struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
1036 struct inode *newinode = NULL;
1037 int rc = -EPERM;
1038 int create_options = CREATE_NOT_DIR | CREATE_OPTION_SPECIAL;
1039 FILE_ALL_INFO *buf = NULL;
1040 struct cifs_io_parms io_parms;
1041 __u32 oplock = 0;
1042 struct cifs_fid fid;
1043 struct cifs_open_parms oparms;
1044 unsigned int bytes_written;
1045 struct win_dev *pdev;
1046 struct kvec iov[2];
1047
1048 if (tcon->unix_ext) {
1049 /*
1050 * SMB1 Unix Extensions: requires server support but
1051 * works with all special files
1052 */
1053 struct cifs_unix_set_info_args args = {
1054 .mode = mode & ~current_umask(),
1055 .ctime = NO_CHANGE_64,
1056 .atime = NO_CHANGE_64,
1057 .mtime = NO_CHANGE_64,
1058 .device = dev,
1059 };
1060 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
1061 args.uid = current_fsuid();
1062 args.gid = current_fsgid();
1063 } else {
1064 args.uid = INVALID_UID; /* no change */
1065 args.gid = INVALID_GID; /* no change */
1066 }
1067 rc = CIFSSMBUnixSetPathInfo(xid, tcon, full_path, &args,
1068 cifs_sb->local_nls,
1069 cifs_remap(cifs_sb));
1070 if (rc)
1071 goto out;
1072
1073 rc = cifs_get_inode_info_unix(&newinode, full_path,
1074 inode->i_sb, xid);
1075
1076 if (rc == 0)
1077 d_instantiate(dentry, newinode);
1078 goto out;
1079 }
1080
1081 /*
1082 * SMB1 SFU emulation: should work with all servers, but only
1083 * support block and char device (no socket & fifo)
1084 */
1085 if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL))
1086 goto out;
1087
1088 if (!S_ISCHR(mode) && !S_ISBLK(mode))
1089 goto out;
1090
1091 cifs_dbg(FYI, "sfu compat create special file\n");
1092
1093 buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
1094 if (buf == NULL) {
1095 rc = -ENOMEM;
1096 goto out;
1097 }
1098
1099 if (backup_cred(cifs_sb))
1100 create_options |= CREATE_OPEN_BACKUP_INTENT;
1101
1102 oparms.tcon = tcon;
1103 oparms.cifs_sb = cifs_sb;
1104 oparms.desired_access = GENERIC_WRITE;
1105 oparms.create_options = create_options;
1106 oparms.disposition = FILE_CREATE;
1107 oparms.path = full_path;
1108 oparms.fid = &fid;
1109 oparms.reconnect = false;
1110
1111 if (tcon->ses->server->oplocks)
1112 oplock = REQ_OPLOCK;
1113 else
1114 oplock = 0;
1115 rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, buf);
1116 if (rc)
1117 goto out;
1118
1119 /*
1120 * BB Do not bother to decode buf since no local inode yet to put
1121 * timestamps in, but we can reuse it safely.
1122 */
1123
1124 pdev = (struct win_dev *)buf;
1125 io_parms.pid = current->tgid;
1126 io_parms.tcon = tcon;
1127 io_parms.offset = 0;
1128 io_parms.length = sizeof(struct win_dev);
1129 iov[1].iov_base = buf;
1130 iov[1].iov_len = sizeof(struct win_dev);
1131 if (S_ISCHR(mode)) {
1132 memcpy(pdev->type, "IntxCHR", 8);
1133 pdev->major = cpu_to_le64(MAJOR(dev));
1134 pdev->minor = cpu_to_le64(MINOR(dev));
1135 rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms,
1136 &bytes_written, iov, 1);
1137 } else if (S_ISBLK(mode)) {
1138 memcpy(pdev->type, "IntxBLK", 8);
1139 pdev->major = cpu_to_le64(MAJOR(dev));
1140 pdev->minor = cpu_to_le64(MINOR(dev));
1141 rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms,
1142 &bytes_written, iov, 1);
1143 }
1144 tcon->ses->server->ops->close(xid, tcon, &fid);
1145 d_drop(dentry);
1146
1147 /* FIXME: add code here to set EAs */
1148out:
1149 kfree(buf);
1150 return rc;
1151}
1152
1153
1154
1030struct smb_version_operations smb1_operations = { 1155struct smb_version_operations smb1_operations = {
1031 .send_cancel = send_nt_cancel, 1156 .send_cancel = send_nt_cancel,
1032 .compare_fids = cifs_compare_fids, 1157 .compare_fids = cifs_compare_fids,
@@ -1110,6 +1235,7 @@ struct smb_version_operations smb1_operations = {
1110 .get_acl_by_fid = get_cifs_acl_by_fid, 1235 .get_acl_by_fid = get_cifs_acl_by_fid,
1111 .set_acl = set_cifs_acl, 1236 .set_acl = set_cifs_acl,
1112#endif /* CIFS_ACL */ 1237#endif /* CIFS_ACL */
1238 .make_node = cifs_make_node,
1113}; 1239};
1114 1240
1115struct smb_version_values smb1_values = { 1241struct smb_version_values smb1_values = {
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index 32dde87feaa9..9a7164c2ea63 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -3767,6 +3767,104 @@ smb2_next_header(char *buf)
3767 return le32_to_cpu(hdr->NextCommand); 3767 return le32_to_cpu(hdr->NextCommand);
3768} 3768}
3769 3769
3770static int
3771smb2_make_node(unsigned int xid, struct inode *inode,
3772 struct dentry *dentry, struct cifs_tcon *tcon,
3773 char *full_path, umode_t mode, dev_t dev)
3774{
3775 struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
3776 int rc = -EPERM;
3777 int create_options = CREATE_NOT_DIR | CREATE_OPTION_SPECIAL;
3778 FILE_ALL_INFO *buf = NULL;
3779 struct cifs_io_parms io_parms;
3780 __u32 oplock = 0;
3781 struct cifs_fid fid;
3782 struct cifs_open_parms oparms;
3783 unsigned int bytes_written;
3784 struct win_dev *pdev;
3785 struct kvec iov[2];
3786
3787 /*
3788 * Check if mounted with mount parm 'sfu' mount parm.
3789 * SFU emulation should work with all servers, but only
3790 * supports block and char device (no socket & fifo),
3791 * and was used by default in earlier versions of Windows
3792 */
3793 if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL))
3794 goto out;
3795
3796 /*
3797 * TODO: Add ability to create instead via reparse point. Windows (e.g.
3798 * their current NFS server) uses this approach to expose special files
3799 * over SMB2/SMB3 and Samba will do this with SMB3.1.1 POSIX Extensions
3800 */
3801
3802 if (!S_ISCHR(mode) && !S_ISBLK(mode))
3803 goto out;
3804
3805 cifs_dbg(FYI, "sfu compat create special file\n");
3806
3807 buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
3808 if (buf == NULL) {
3809 rc = -ENOMEM;
3810 goto out;
3811 }
3812
3813 if (backup_cred(cifs_sb))
3814 create_options |= CREATE_OPEN_BACKUP_INTENT;
3815
3816 oparms.tcon = tcon;
3817 oparms.cifs_sb = cifs_sb;
3818 oparms.desired_access = GENERIC_WRITE;
3819 oparms.create_options = create_options;
3820 oparms.disposition = FILE_CREATE;
3821 oparms.path = full_path;
3822 oparms.fid = &fid;
3823 oparms.reconnect = false;
3824
3825 if (tcon->ses->server->oplocks)
3826 oplock = REQ_OPLOCK;
3827 else
3828 oplock = 0;
3829 rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, buf);
3830 if (rc)
3831 goto out;
3832
3833 /*
3834 * BB Do not bother to decode buf since no local inode yet to put
3835 * timestamps in, but we can reuse it safely.
3836 */
3837
3838 pdev = (struct win_dev *)buf;
3839 io_parms.pid = current->tgid;
3840 io_parms.tcon = tcon;
3841 io_parms.offset = 0;
3842 io_parms.length = sizeof(struct win_dev);
3843 iov[1].iov_base = buf;
3844 iov[1].iov_len = sizeof(struct win_dev);
3845 if (S_ISCHR(mode)) {
3846 memcpy(pdev->type, "IntxCHR", 8);
3847 pdev->major = cpu_to_le64(MAJOR(dev));
3848 pdev->minor = cpu_to_le64(MINOR(dev));
3849 rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms,
3850 &bytes_written, iov, 1);
3851 } else if (S_ISBLK(mode)) {
3852 memcpy(pdev->type, "IntxBLK", 8);
3853 pdev->major = cpu_to_le64(MAJOR(dev));
3854 pdev->minor = cpu_to_le64(MINOR(dev));
3855 rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms,
3856 &bytes_written, iov, 1);
3857 }
3858 tcon->ses->server->ops->close(xid, tcon, &fid);
3859 d_drop(dentry);
3860
3861 /* FIXME: add code here to set EAs */
3862out:
3863 kfree(buf);
3864 return rc;
3865}
3866
3867
3770struct smb_version_operations smb20_operations = { 3868struct smb_version_operations smb20_operations = {
3771 .compare_fids = smb2_compare_fids, 3869 .compare_fids = smb2_compare_fids,
3772 .setup_request = smb2_setup_request, 3870 .setup_request = smb2_setup_request,
@@ -3861,6 +3959,7 @@ struct smb_version_operations smb20_operations = {
3861#endif /* CIFS_ACL */ 3959#endif /* CIFS_ACL */
3862 .next_header = smb2_next_header, 3960 .next_header = smb2_next_header,
3863 .ioctl_query_info = smb2_ioctl_query_info, 3961 .ioctl_query_info = smb2_ioctl_query_info,
3962 .make_node = smb2_make_node,
3864}; 3963};
3865 3964
3866struct smb_version_operations smb21_operations = { 3965struct smb_version_operations smb21_operations = {
@@ -3959,6 +4058,7 @@ struct smb_version_operations smb21_operations = {
3959#endif /* CIFS_ACL */ 4058#endif /* CIFS_ACL */
3960 .next_header = smb2_next_header, 4059 .next_header = smb2_next_header,
3961 .ioctl_query_info = smb2_ioctl_query_info, 4060 .ioctl_query_info = smb2_ioctl_query_info,
4061 .make_node = smb2_make_node,
3962}; 4062};
3963 4063
3964struct smb_version_operations smb30_operations = { 4064struct smb_version_operations smb30_operations = {
@@ -4066,6 +4166,7 @@ struct smb_version_operations smb30_operations = {
4066#endif /* CIFS_ACL */ 4166#endif /* CIFS_ACL */
4067 .next_header = smb2_next_header, 4167 .next_header = smb2_next_header,
4068 .ioctl_query_info = smb2_ioctl_query_info, 4168 .ioctl_query_info = smb2_ioctl_query_info,
4169 .make_node = smb2_make_node,
4069}; 4170};
4070 4171
4071struct smb_version_operations smb311_operations = { 4172struct smb_version_operations smb311_operations = {
@@ -4174,6 +4275,7 @@ struct smb_version_operations smb311_operations = {
4174#endif /* CIFS_ACL */ 4275#endif /* CIFS_ACL */
4175 .next_header = smb2_next_header, 4276 .next_header = smb2_next_header,
4176 .ioctl_query_info = smb2_ioctl_query_info, 4277 .ioctl_query_info = smb2_ioctl_query_info,
4278 .make_node = smb2_make_node,
4177}; 4279};
4178 4280
4179struct smb_version_values smb20_values = { 4281struct smb_version_values smb20_values = {