aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShirish Pargaonkar <shirishpargaonkar@gmail.com>2011-09-26 10:56:44 -0400
committerSteve French <smfrench@gmail.com>2011-10-13 00:42:17 -0400
commit3d3ea8e64efbeb3e4289675dbbfab82333395642 (patch)
tree3f7c52039dceefeae2abf010a1a3ec8abef0c459
parent8bc4392a1e50f346e97f8777aaefd9cfc3d45c9f (diff)
cifs: Add mount options for backup intent (try #6)
Add mount options backupuid and backugid. It allows an authenticated user to access files with the intent to back them up including their ACLs, who may not have access permission but has "Backup files and directories user right" on them (by virtue of being part of the built-in group Backup Operators. When mount options backupuid is specified, cifs client restricts the use of backup intents to the user whose effective user id is specified along with the mount option. When mount options backupgid is specified, cifs client restricts the use of backup intents to the users whose effective user id belongs to the group id specified along with the mount option. If an authenticated user is not part of the built-in group Backup Operators at the server, access to such files is denied, even if allowed by the client. Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com> Reviewed-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: Steve French <smfrench@gmail.com>
-rw-r--r--fs/cifs/cifs_fs_sb.h4
-rw-r--r--fs/cifs/cifsacl.c18
-rw-r--r--fs/cifs/cifsglob.h7
-rw-r--r--fs/cifs/cifsproto.h1
-rw-r--r--fs/cifs/connect.c27
-rw-r--r--fs/cifs/dir.c10
-rw-r--r--fs/cifs/file.c12
-rw-r--r--fs/cifs/link.c17
-rw-r--r--fs/cifs/misc.c15
9 files changed, 95 insertions, 16 deletions
diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h
index 7260e11e21f8..500d65859279 100644
--- a/fs/cifs/cifs_fs_sb.h
+++ b/fs/cifs/cifs_fs_sb.h
@@ -43,6 +43,8 @@
43#define CIFS_MOUNT_STRICT_IO 0x40000 /* strict cache mode */ 43#define CIFS_MOUNT_STRICT_IO 0x40000 /* strict cache mode */
44#define CIFS_MOUNT_RWPIDFORWARD 0x80000 /* use pid forwarding for rw */ 44#define CIFS_MOUNT_RWPIDFORWARD 0x80000 /* use pid forwarding for rw */
45#define CIFS_MOUNT_POSIXACL 0x100000 /* mirror of MS_POSIXACL in mnt_cifs_flags */ 45#define CIFS_MOUNT_POSIXACL 0x100000 /* mirror of MS_POSIXACL in mnt_cifs_flags */
46#define CIFS_MOUNT_CIFS_BACKUPUID 0x200000 /* backup intent bit for a user */
47#define CIFS_MOUNT_CIFS_BACKUPGID 0x400000 /* backup intent bit for a group */
46 48
47struct cifs_sb_info { 49struct cifs_sb_info {
48 struct rb_root tlink_tree; 50 struct rb_root tlink_tree;
@@ -55,6 +57,8 @@ struct cifs_sb_info {
55 atomic_t active; 57 atomic_t active;
56 uid_t mnt_uid; 58 uid_t mnt_uid;
57 gid_t mnt_gid; 59 gid_t mnt_gid;
60 uid_t mnt_backupuid;
61 gid_t mnt_backupgid;
58 mode_t mnt_file_mode; 62 mode_t mnt_file_mode;
59 mode_t mnt_dir_mode; 63 mode_t mnt_dir_mode;
60 unsigned int mnt_cifs_flags; 64 unsigned int mnt_cifs_flags;
diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c
index d0f59faefb78..b244e07c3048 100644
--- a/fs/cifs/cifsacl.c
+++ b/fs/cifs/cifsacl.c
@@ -945,7 +945,7 @@ static struct cifs_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb,
945{ 945{
946 struct cifs_ntsd *pntsd = NULL; 946 struct cifs_ntsd *pntsd = NULL;
947 int oplock = 0; 947 int oplock = 0;
948 int xid, rc; 948 int xid, rc, create_options = 0;
949 __u16 fid; 949 __u16 fid;
950 struct cifs_tcon *tcon; 950 struct cifs_tcon *tcon;
951 struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); 951 struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
@@ -956,9 +956,12 @@ static struct cifs_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb,
956 tcon = tlink_tcon(tlink); 956 tcon = tlink_tcon(tlink);
957 xid = GetXid(); 957 xid = GetXid();
958 958
959 rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, READ_CONTROL, 0, 959 if (backup_cred(cifs_sb))
960 &fid, &oplock, NULL, cifs_sb->local_nls, 960 create_options |= CREATE_OPEN_BACKUP_INTENT;
961 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); 961
962 rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, READ_CONTROL,
963 create_options, &fid, &oplock, NULL, cifs_sb->local_nls,
964 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
962 if (!rc) { 965 if (!rc) {
963 rc = CIFSSMBGetCIFSACL(xid, tcon, fid, &pntsd, pacllen); 966 rc = CIFSSMBGetCIFSACL(xid, tcon, fid, &pntsd, pacllen);
964 CIFSSMBClose(xid, tcon, fid); 967 CIFSSMBClose(xid, tcon, fid);
@@ -995,7 +998,7 @@ static int set_cifs_acl_by_path(struct cifs_sb_info *cifs_sb, const char *path,
995 struct cifs_ntsd *pnntsd, u32 acllen) 998 struct cifs_ntsd *pnntsd, u32 acllen)
996{ 999{
997 int oplock = 0; 1000 int oplock = 0;
998 int xid, rc; 1001 int xid, rc, create_options = 0;
999 __u16 fid; 1002 __u16 fid;
1000 struct cifs_tcon *tcon; 1003 struct cifs_tcon *tcon;
1001 struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); 1004 struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
@@ -1006,7 +1009,10 @@ static int set_cifs_acl_by_path(struct cifs_sb_info *cifs_sb, const char *path,
1006 tcon = tlink_tcon(tlink); 1009 tcon = tlink_tcon(tlink);
1007 xid = GetXid(); 1010 xid = GetXid();
1008 1011
1009 rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, WRITE_DAC, 0, 1012 if (backup_cred(cifs_sb))
1013 create_options |= CREATE_OPEN_BACKUP_INTENT;
1014
1015 rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, WRITE_DAC, create_options,
1010 &fid, &oplock, NULL, cifs_sb->local_nls, 1016 &fid, &oplock, NULL, cifs_sb->local_nls,
1011 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); 1017 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
1012 if (rc) { 1018 if (rc) {
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index d734dee9d495..9551437a2498 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -167,6 +167,8 @@ struct smb_vol {
167 uid_t cred_uid; 167 uid_t cred_uid;
168 uid_t linux_uid; 168 uid_t linux_uid;
169 gid_t linux_gid; 169 gid_t linux_gid;
170 uid_t backupuid;
171 gid_t backupgid;
170 mode_t file_mode; 172 mode_t file_mode;
171 mode_t dir_mode; 173 mode_t dir_mode;
172 unsigned secFlg; 174 unsigned secFlg;
@@ -179,6 +181,8 @@ struct smb_vol {
179 bool noperm:1; 181 bool noperm:1;
180 bool no_psx_acl:1; /* set if posix acl support should be disabled */ 182 bool no_psx_acl:1; /* set if posix acl support should be disabled */
181 bool cifs_acl:1; 183 bool cifs_acl:1;
184 bool backupuid_specified; /* mount option backupuid is specified */
185 bool backupgid_specified; /* mount option backupgid is specified */
182 bool no_xattr:1; /* set if xattr (EA) support should be disabled*/ 186 bool no_xattr:1; /* set if xattr (EA) support should be disabled*/
183 bool server_ino:1; /* use inode numbers from server ie UniqueId */ 187 bool server_ino:1; /* use inode numbers from server ie UniqueId */
184 bool direct_io:1; 188 bool direct_io:1;
@@ -219,7 +223,8 @@ struct smb_vol {
219 CIFS_MOUNT_OVERR_GID | CIFS_MOUNT_DYNPERM | \ 223 CIFS_MOUNT_OVERR_GID | CIFS_MOUNT_DYNPERM | \
220 CIFS_MOUNT_NOPOSIXBRL | CIFS_MOUNT_NOSSYNC | \ 224 CIFS_MOUNT_NOPOSIXBRL | CIFS_MOUNT_NOSSYNC | \
221 CIFS_MOUNT_FSCACHE | CIFS_MOUNT_MF_SYMLINKS | \ 225 CIFS_MOUNT_FSCACHE | CIFS_MOUNT_MF_SYMLINKS | \
222 CIFS_MOUNT_MULTIUSER | CIFS_MOUNT_STRICT_IO) 226 CIFS_MOUNT_MULTIUSER | CIFS_MOUNT_STRICT_IO | \
227 CIFS_MOUNT_CIFS_BACKUPUID | CIFS_MOUNT_CIFS_BACKUPGID)
223 228
224#define CIFS_MS_MASK (MS_RDONLY | MS_MANDLOCK | MS_NOEXEC | MS_NOSUID | \ 229#define CIFS_MS_MASK (MS_RDONLY | MS_MANDLOCK | MS_NOEXEC | MS_NOSUID | \
225 MS_NODEV | MS_SYNCHRONOUS) 230 MS_NODEV | MS_SYNCHRONOUS)
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index 03dc945d94a3..9ddb1eccde69 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -90,6 +90,7 @@ extern int SendReceiveBlockingLock(const unsigned int xid,
90extern int checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length); 90extern int checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length);
91extern bool is_valid_oplock_break(struct smb_hdr *smb, 91extern bool is_valid_oplock_break(struct smb_hdr *smb,
92 struct TCP_Server_Info *); 92 struct TCP_Server_Info *);
93extern bool backup_cred(struct cifs_sb_info *);
93extern bool is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof); 94extern bool is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof);
94extern void cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset, 95extern void cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset,
95 unsigned int bytes_written); 96 unsigned int bytes_written);
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index abbc6c3fe3f1..70dd2c418276 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -831,6 +831,7 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
831{ 831{
832 char *value, *data, *end; 832 char *value, *data, *end;
833 char *mountdata_copy = NULL, *options; 833 char *mountdata_copy = NULL, *options;
834 int err;
834 unsigned int temp_len, i, j; 835 unsigned int temp_len, i, j;
835 char separator[2]; 836 char separator[2];
836 short int override_uid = -1; 837 short int override_uid = -1;
@@ -887,6 +888,8 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
887 cFYI(1, "Null separator not allowed"); 888 cFYI(1, "Null separator not allowed");
888 } 889 }
889 } 890 }
891 vol->backupuid_specified = false; /* no backup intent for a user */
892 vol->backupgid_specified = false; /* no backup intent for a group */
890 893
891 while ((data = strsep(&options, separator)) != NULL) { 894 while ((data = strsep(&options, separator)) != NULL) {
892 if (!*data) 895 if (!*data)
@@ -1446,6 +1449,22 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
1446 vol->mfsymlinks = true; 1449 vol->mfsymlinks = true;
1447 } else if (strnicmp(data, "multiuser", 8) == 0) { 1450 } else if (strnicmp(data, "multiuser", 8) == 0) {
1448 vol->multiuser = true; 1451 vol->multiuser = true;
1452 } else if (!strnicmp(data, "backupuid", 9) && value && *value) {
1453 err = kstrtouint(value, 0, &vol->backupuid);
1454 if (err < 0) {
1455 cERROR(1, "%s: Invalid backupuid value",
1456 __func__);
1457 goto cifs_parse_mount_err;
1458 }
1459 vol->backupuid_specified = true;
1460 } else if (!strnicmp(data, "backupgid", 9) && value && *value) {
1461 err = kstrtouint(value, 0, &vol->backupgid);
1462 if (err < 0) {
1463 cERROR(1, "%s: Invalid backupgid value",
1464 __func__);
1465 goto cifs_parse_mount_err;
1466 }
1467 vol->backupgid_specified = true;
1449 } else 1468 } else
1450 printk(KERN_WARNING "CIFS: Unknown mount option %s\n", 1469 printk(KERN_WARNING "CIFS: Unknown mount option %s\n",
1451 data); 1470 data);
@@ -2737,6 +2756,10 @@ void cifs_setup_cifs_sb(struct smb_vol *pvolume_info,
2737 2756
2738 cifs_sb->mnt_uid = pvolume_info->linux_uid; 2757 cifs_sb->mnt_uid = pvolume_info->linux_uid;
2739 cifs_sb->mnt_gid = pvolume_info->linux_gid; 2758 cifs_sb->mnt_gid = pvolume_info->linux_gid;
2759 if (pvolume_info->backupuid_specified)
2760 cifs_sb->mnt_backupuid = pvolume_info->backupuid;
2761 if (pvolume_info->backupgid_specified)
2762 cifs_sb->mnt_backupgid = pvolume_info->backupgid;
2740 cifs_sb->mnt_file_mode = pvolume_info->file_mode; 2763 cifs_sb->mnt_file_mode = pvolume_info->file_mode;
2741 cifs_sb->mnt_dir_mode = pvolume_info->dir_mode; 2764 cifs_sb->mnt_dir_mode = pvolume_info->dir_mode;
2742 cFYI(1, "file mode: 0x%x dir mode: 0x%x", 2765 cFYI(1, "file mode: 0x%x dir mode: 0x%x",
@@ -2767,6 +2790,10 @@ void cifs_setup_cifs_sb(struct smb_vol *pvolume_info,
2767 cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_RWPIDFORWARD; 2790 cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_RWPIDFORWARD;
2768 if (pvolume_info->cifs_acl) 2791 if (pvolume_info->cifs_acl)
2769 cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL; 2792 cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL;
2793 if (pvolume_info->backupuid_specified)
2794 cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_BACKUPUID;
2795 if (pvolume_info->backupgid_specified)
2796 cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_BACKUPGID;
2770 if (pvolume_info->override_uid) 2797 if (pvolume_info->override_uid)
2771 cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_UID; 2798 cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_UID;
2772 if (pvolume_info->override_gid) 2799 if (pvolume_info->override_gid)
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index 4dd5333753fa..0c8098d54d2b 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -244,6 +244,9 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
244 if (!tcon->unix_ext && (mode & S_IWUGO) == 0) 244 if (!tcon->unix_ext && (mode & S_IWUGO) == 0)
245 create_options |= CREATE_OPTION_READONLY; 245 create_options |= CREATE_OPTION_READONLY;
246 246
247 if (backup_cred(cifs_sb))
248 create_options |= CREATE_OPEN_BACKUP_INTENT;
249
247 if (tcon->ses->capabilities & CAP_NT_SMBS) 250 if (tcon->ses->capabilities & CAP_NT_SMBS)
248 rc = CIFSSMBOpen(xid, tcon, full_path, disposition, 251 rc = CIFSSMBOpen(xid, tcon, full_path, disposition,
249 desiredAccess, create_options, 252 desiredAccess, create_options,
@@ -357,6 +360,7 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,
357{ 360{
358 int rc = -EPERM; 361 int rc = -EPERM;
359 int xid; 362 int xid;
363 int create_options = CREATE_NOT_DIR | CREATE_OPTION_SPECIAL;
360 struct cifs_sb_info *cifs_sb; 364 struct cifs_sb_info *cifs_sb;
361 struct tcon_link *tlink; 365 struct tcon_link *tlink;
362 struct cifs_tcon *pTcon; 366 struct cifs_tcon *pTcon;
@@ -431,9 +435,11 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,
431 return rc; 435 return rc;
432 } 436 }
433 437
434 /* FIXME: would WRITE_OWNER | WRITE_DAC be better? */ 438 if (backup_cred(cifs_sb))
439 create_options |= CREATE_OPEN_BACKUP_INTENT;
440
435 rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_CREATE, 441 rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_CREATE,
436 GENERIC_WRITE, CREATE_NOT_DIR | CREATE_OPTION_SPECIAL, 442 GENERIC_WRITE, create_options,
437 &fileHandle, &oplock, buf, cifs_sb->local_nls, 443 &fileHandle, &oplock, buf, cifs_sb->local_nls,
438 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); 444 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
439 if (rc) 445 if (rc)
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 8e184150cfb5..237192ae7587 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -174,6 +174,7 @@ cifs_nt_open(char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb,
174 int rc; 174 int rc;
175 int desiredAccess; 175 int desiredAccess;
176 int disposition; 176 int disposition;
177 int create_options = CREATE_NOT_DIR;
177 FILE_ALL_INFO *buf; 178 FILE_ALL_INFO *buf;
178 179
179 desiredAccess = cifs_convert_flags(f_flags); 180 desiredAccess = cifs_convert_flags(f_flags);
@@ -210,9 +211,12 @@ cifs_nt_open(char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb,
210 if (!buf) 211 if (!buf)
211 return -ENOMEM; 212 return -ENOMEM;
212 213
214 if (backup_cred(cifs_sb))
215 create_options |= CREATE_OPEN_BACKUP_INTENT;
216
213 if (tcon->ses->capabilities & CAP_NT_SMBS) 217 if (tcon->ses->capabilities & CAP_NT_SMBS)
214 rc = CIFSSMBOpen(xid, tcon, full_path, disposition, 218 rc = CIFSSMBOpen(xid, tcon, full_path, disposition,
215 desiredAccess, CREATE_NOT_DIR, pnetfid, poplock, buf, 219 desiredAccess, create_options, pnetfid, poplock, buf,
216 cifs_sb->local_nls, cifs_sb->mnt_cifs_flags 220 cifs_sb->local_nls, cifs_sb->mnt_cifs_flags
217 & CIFS_MOUNT_MAP_SPECIAL_CHR); 221 & CIFS_MOUNT_MAP_SPECIAL_CHR);
218 else 222 else
@@ -465,6 +469,7 @@ static int cifs_reopen_file(struct cifsFileInfo *pCifsFile, bool can_flush)
465 char *full_path = NULL; 469 char *full_path = NULL;
466 int desiredAccess; 470 int desiredAccess;
467 int disposition = FILE_OPEN; 471 int disposition = FILE_OPEN;
472 int create_options = CREATE_NOT_DIR;
468 __u16 netfid; 473 __u16 netfid;
469 474
470 xid = GetXid(); 475 xid = GetXid();
@@ -524,6 +529,9 @@ static int cifs_reopen_file(struct cifsFileInfo *pCifsFile, bool can_flush)
524 529
525 desiredAccess = cifs_convert_flags(pCifsFile->f_flags); 530 desiredAccess = cifs_convert_flags(pCifsFile->f_flags);
526 531
532 if (backup_cred(cifs_sb))
533 create_options |= CREATE_OPEN_BACKUP_INTENT;
534
527 /* Can not refresh inode by passing in file_info buf to be returned 535 /* Can not refresh inode by passing in file_info buf to be returned
528 by SMBOpen and then calling get_inode_info with returned buf 536 by SMBOpen and then calling get_inode_info with returned buf
529 since file might have write behind data that needs to be flushed 537 since file might have write behind data that needs to be flushed
@@ -531,7 +539,7 @@ static int cifs_reopen_file(struct cifsFileInfo *pCifsFile, bool can_flush)
531 that inode was not dirty locally we could do this */ 539 that inode was not dirty locally we could do this */
532 540
533 rc = CIFSSMBOpen(xid, tcon, full_path, disposition, desiredAccess, 541 rc = CIFSSMBOpen(xid, tcon, full_path, disposition, desiredAccess,
534 CREATE_NOT_DIR, &netfid, &oplock, NULL, 542 create_options, &netfid, &oplock, NULL,
535 cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & 543 cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
536 CIFS_MOUNT_MAP_SPECIAL_CHR); 544 CIFS_MOUNT_MAP_SPECIAL_CHR);
537 if (rc) { 545 if (rc) {
diff --git a/fs/cifs/link.c b/fs/cifs/link.c
index db3f18cdf024..8693b5d0e180 100644
--- a/fs/cifs/link.c
+++ b/fs/cifs/link.c
@@ -183,14 +183,20 @@ CIFSFormatMFSymlink(u8 *buf, unsigned int buf_len, const char *link_str)
183static int 183static int
184CIFSCreateMFSymLink(const int xid, struct cifs_tcon *tcon, 184CIFSCreateMFSymLink(const int xid, struct cifs_tcon *tcon,
185 const char *fromName, const char *toName, 185 const char *fromName, const char *toName,
186 const struct nls_table *nls_codepage, int remap) 186 struct cifs_sb_info *cifs_sb)
187{ 187{
188 int rc; 188 int rc;
189 int oplock = 0; 189 int oplock = 0;
190 int remap;
191 int create_options = CREATE_NOT_DIR;
190 __u16 netfid = 0; 192 __u16 netfid = 0;
191 u8 *buf; 193 u8 *buf;
192 unsigned int bytes_written = 0; 194 unsigned int bytes_written = 0;
193 struct cifs_io_parms io_parms; 195 struct cifs_io_parms io_parms;
196 struct nls_table *nls_codepage;
197
198 nls_codepage = cifs_sb->local_nls;
199 remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR;
194 200
195 buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL); 201 buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
196 if (!buf) 202 if (!buf)
@@ -202,8 +208,11 @@ CIFSCreateMFSymLink(const int xid, struct cifs_tcon *tcon,
202 return rc; 208 return rc;
203 } 209 }
204 210
211 if (backup_cred(cifs_sb))
212 create_options |= CREATE_OPEN_BACKUP_INTENT;
213
205 rc = CIFSSMBOpen(xid, tcon, fromName, FILE_CREATE, GENERIC_WRITE, 214 rc = CIFSSMBOpen(xid, tcon, fromName, FILE_CREATE, GENERIC_WRITE,
206 CREATE_NOT_DIR, &netfid, &oplock, NULL, 215 create_options, &netfid, &oplock, NULL,
207 nls_codepage, remap); 216 nls_codepage, remap);
208 if (rc != 0) { 217 if (rc != 0) {
209 kfree(buf); 218 kfree(buf);
@@ -559,9 +568,7 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname)
559 /* BB what if DFS and this volume is on different share? BB */ 568 /* BB what if DFS and this volume is on different share? BB */
560 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) 569 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS)
561 rc = CIFSCreateMFSymLink(xid, pTcon, full_path, symname, 570 rc = CIFSCreateMFSymLink(xid, pTcon, full_path, symname,
562 cifs_sb->local_nls, 571 cifs_sb);
563 cifs_sb->mnt_cifs_flags &
564 CIFS_MOUNT_MAP_SPECIAL_CHR);
565 else if (pTcon->unix_ext) 572 else if (pTcon->unix_ext)
566 rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname, 573 rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname,
567 cifs_sb->local_nls); 574 cifs_sb->local_nls);
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 4a1801b3195f..703ef5c6fdb1 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -675,3 +675,18 @@ void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock)
675 cinode->clientCanCacheRead = false; 675 cinode->clientCanCacheRead = false;
676 } 676 }
677} 677}
678
679bool
680backup_cred(struct cifs_sb_info *cifs_sb)
681{
682 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPUID) {
683 if (cifs_sb->mnt_backupuid == current_fsuid())
684 return true;
685 }
686 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPGID) {
687 if (in_group_p(cifs_sb->mnt_backupgid))
688 return true;
689 }
690
691 return false;
692}