cifs: Add mount options for backup intent (try #6)
authorShirish Pargaonkar <shirishpargaonkar@gmail.com>
Mon, 26 Sep 2011 14:56:44 +0000 (09:56 -0500)
committerSteve French <smfrench@gmail.com>
Thu, 13 Oct 2011 04:42:17 +0000 (23:42 -0500)
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>
fs/cifs/cifs_fs_sb.h
fs/cifs/cifsacl.c
fs/cifs/cifsglob.h
fs/cifs/cifsproto.h
fs/cifs/connect.c
fs/cifs/dir.c
fs/cifs/file.c
fs/cifs/link.c
fs/cifs/misc.c

index 7260e11e21f8429304b519b2ec0f5aeb477ce0b9..500d65859279eb817a7f14e478f789058339f182 100644 (file)
@@ -43,6 +43,8 @@
 #define CIFS_MOUNT_STRICT_IO   0x40000 /* strict cache mode */
 #define CIFS_MOUNT_RWPIDFORWARD        0x80000 /* use pid forwarding for rw */
 #define CIFS_MOUNT_POSIXACL    0x100000 /* mirror of MS_POSIXACL in mnt_cifs_flags */
+#define CIFS_MOUNT_CIFS_BACKUPUID 0x200000 /* backup intent bit for a user */
+#define CIFS_MOUNT_CIFS_BACKUPGID 0x400000 /* backup intent bit for a group */
 
 struct cifs_sb_info {
        struct rb_root tlink_tree;
@@ -55,6 +57,8 @@ struct cifs_sb_info {
        atomic_t active;
        uid_t   mnt_uid;
        gid_t   mnt_gid;
+       uid_t   mnt_backupuid;
+       gid_t   mnt_backupgid;
        mode_t  mnt_file_mode;
        mode_t  mnt_dir_mode;
        unsigned int mnt_cifs_flags;
index d0f59faefb7893f2189cca07e660fb424bd3d629..b244e07c3048e37795f8ef2286e51ea889aec7ff 100644 (file)
@@ -945,7 +945,7 @@ static struct cifs_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb,
 {
        struct cifs_ntsd *pntsd = NULL;
        int oplock = 0;
-       int xid, rc;
+       int xid, rc, create_options = 0;
        __u16 fid;
        struct cifs_tcon *tcon;
        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,
        tcon = tlink_tcon(tlink);
        xid = GetXid();
 
-       rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, READ_CONTROL, 0,
-                        &fid, &oplock, NULL, cifs_sb->local_nls,
-                        cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+       if (backup_cred(cifs_sb))
+               create_options |= CREATE_OPEN_BACKUP_INTENT;
+
+       rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, READ_CONTROL,
+                       create_options, &fid, &oplock, NULL, cifs_sb->local_nls,
+                       cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
        if (!rc) {
                rc = CIFSSMBGetCIFSACL(xid, tcon, fid, &pntsd, pacllen);
                CIFSSMBClose(xid, tcon, fid);
@@ -995,7 +998,7 @@ static int set_cifs_acl_by_path(struct cifs_sb_info *cifs_sb, const char *path,
                struct cifs_ntsd *pnntsd, u32 acllen)
 {
        int oplock = 0;
-       int xid, rc;
+       int xid, rc, create_options = 0;
        __u16 fid;
        struct cifs_tcon *tcon;
        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,
        tcon = tlink_tcon(tlink);
        xid = GetXid();
 
-       rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, WRITE_DAC, 0,
+       if (backup_cred(cifs_sb))
+               create_options |= CREATE_OPEN_BACKUP_INTENT;
+
+       rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, WRITE_DAC, create_options,
                         &fid, &oplock, NULL, cifs_sb->local_nls,
                         cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
        if (rc) {
index d734dee9d495d77dce5631a13bf98a6224df95e1..9551437a249829b1b17e4802db50146effec9624 100644 (file)
@@ -167,6 +167,8 @@ struct smb_vol {
        uid_t cred_uid;
        uid_t linux_uid;
        gid_t linux_gid;
+       uid_t backupuid;
+       gid_t backupgid;
        mode_t file_mode;
        mode_t dir_mode;
        unsigned secFlg;
@@ -179,6 +181,8 @@ struct smb_vol {
        bool noperm:1;
        bool no_psx_acl:1; /* set if posix acl support should be disabled */
        bool cifs_acl:1;
+       bool backupuid_specified; /* mount option  backupuid  is specified */
+       bool backupgid_specified; /* mount option  backupgid  is specified */
        bool no_xattr:1;   /* set if xattr (EA) support should be disabled*/
        bool server_ino:1; /* use inode numbers from server ie UniqueId */
        bool direct_io:1;
@@ -219,7 +223,8 @@ struct smb_vol {
                         CIFS_MOUNT_OVERR_GID | CIFS_MOUNT_DYNPERM | \
                         CIFS_MOUNT_NOPOSIXBRL | CIFS_MOUNT_NOSSYNC | \
                         CIFS_MOUNT_FSCACHE | CIFS_MOUNT_MF_SYMLINKS | \
-                        CIFS_MOUNT_MULTIUSER | CIFS_MOUNT_STRICT_IO)
+                        CIFS_MOUNT_MULTIUSER | CIFS_MOUNT_STRICT_IO | \
+                        CIFS_MOUNT_CIFS_BACKUPUID | CIFS_MOUNT_CIFS_BACKUPGID)
 
 #define CIFS_MS_MASK (MS_RDONLY | MS_MANDLOCK | MS_NOEXEC | MS_NOSUID | \
                      MS_NODEV | MS_SYNCHRONOUS)
index 03dc945d94a3a39eeb1351ecbf8a8c4eed9c2537..9ddb1eccde69c8cf6f1b4514fcdbbfbf42d43823 100644 (file)
@@ -90,6 +90,7 @@ extern int SendReceiveBlockingLock(const unsigned int xid,
 extern int checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length);
 extern bool is_valid_oplock_break(struct smb_hdr *smb,
                                  struct TCP_Server_Info *);
+extern bool backup_cred(struct cifs_sb_info *);
 extern bool is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof);
 extern void cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset,
                            unsigned int bytes_written);
index abbc6c3fe3f12b172fbbf48c395c6e4f0423250e..70dd2c4182761cc01801dedf8a8807a5fea0b2b5 100644 (file)
@@ -831,6 +831,7 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
 {
        char *value, *data, *end;
        char *mountdata_copy = NULL, *options;
+       int err;
        unsigned int  temp_len, i, j;
        char separator[2];
        short int override_uid = -1;
@@ -887,6 +888,8 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
                        cFYI(1, "Null separator not allowed");
                }
        }
+       vol->backupuid_specified = false; /* no backup intent for a user */
+       vol->backupgid_specified = false; /* no backup intent for a group */
 
        while ((data = strsep(&options, separator)) != NULL) {
                if (!*data)
@@ -1446,6 +1449,22 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
                        vol->mfsymlinks = true;
                } else if (strnicmp(data, "multiuser", 8) == 0) {
                        vol->multiuser = true;
+               } else if (!strnicmp(data, "backupuid", 9) && value && *value) {
+                       err = kstrtouint(value, 0, &vol->backupuid);
+                       if (err < 0) {
+                               cERROR(1, "%s: Invalid backupuid value",
+                                       __func__);
+                               goto cifs_parse_mount_err;
+                       }
+                       vol->backupuid_specified = true;
+               } else if (!strnicmp(data, "backupgid", 9) && value && *value) {
+                       err = kstrtouint(value, 0, &vol->backupgid);
+                       if (err < 0) {
+                               cERROR(1, "%s: Invalid backupgid value",
+                                       __func__);
+                               goto cifs_parse_mount_err;
+                       }
+                       vol->backupgid_specified = true;
                } else
                        printk(KERN_WARNING "CIFS: Unknown mount option %s\n",
                                                data);
@@ -2737,6 +2756,10 @@ void cifs_setup_cifs_sb(struct smb_vol *pvolume_info,
 
        cifs_sb->mnt_uid = pvolume_info->linux_uid;
        cifs_sb->mnt_gid = pvolume_info->linux_gid;
+       if (pvolume_info->backupuid_specified)
+               cifs_sb->mnt_backupuid = pvolume_info->backupuid;
+       if (pvolume_info->backupgid_specified)
+               cifs_sb->mnt_backupgid = pvolume_info->backupgid;
        cifs_sb->mnt_file_mode = pvolume_info->file_mode;
        cifs_sb->mnt_dir_mode = pvolume_info->dir_mode;
        cFYI(1, "file mode: 0x%x  dir mode: 0x%x",
@@ -2767,6 +2790,10 @@ void cifs_setup_cifs_sb(struct smb_vol *pvolume_info,
                cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_RWPIDFORWARD;
        if (pvolume_info->cifs_acl)
                cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL;
+       if (pvolume_info->backupuid_specified)
+               cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_BACKUPUID;
+       if (pvolume_info->backupgid_specified)
+               cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_BACKUPGID;
        if (pvolume_info->override_uid)
                cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_UID;
        if (pvolume_info->override_gid)
index 4dd5333753fa771cbe7d245334accf7e0cdaf408..0c8098d54d2b80e44e51aa66097475bd0d3ae584 100644 (file)
@@ -244,6 +244,9 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
        if (!tcon->unix_ext && (mode & S_IWUGO) == 0)
                create_options |= CREATE_OPTION_READONLY;
 
+       if (backup_cred(cifs_sb))
+               create_options |= CREATE_OPEN_BACKUP_INTENT;
+
        if (tcon->ses->capabilities & CAP_NT_SMBS)
                rc = CIFSSMBOpen(xid, tcon, full_path, disposition,
                         desiredAccess, create_options,
@@ -357,6 +360,7 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,
 {
        int rc = -EPERM;
        int xid;
+       int create_options = CREATE_NOT_DIR | CREATE_OPTION_SPECIAL;
        struct cifs_sb_info *cifs_sb;
        struct tcon_link *tlink;
        struct cifs_tcon *pTcon;
@@ -431,9 +435,11 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,
                return rc;
        }
 
-       /* FIXME: would WRITE_OWNER | WRITE_DAC be better? */
+       if (backup_cred(cifs_sb))
+               create_options |= CREATE_OPEN_BACKUP_INTENT;
+
        rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_CREATE,
-                        GENERIC_WRITE, CREATE_NOT_DIR | CREATE_OPTION_SPECIAL,
+                        GENERIC_WRITE, create_options,
                         &fileHandle, &oplock, buf, cifs_sb->local_nls,
                         cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
        if (rc)
index 8e184150cfb579be04a4d8c988cecb83ce2c0d5e..237192ae7587b444dfcc1c659bc981baa062f74f 100644 (file)
@@ -174,6 +174,7 @@ cifs_nt_open(char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb,
        int rc;
        int desiredAccess;
        int disposition;
+       int create_options = CREATE_NOT_DIR;
        FILE_ALL_INFO *buf;
 
        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,
        if (!buf)
                return -ENOMEM;
 
+       if (backup_cred(cifs_sb))
+               create_options |= CREATE_OPEN_BACKUP_INTENT;
+
        if (tcon->ses->capabilities & CAP_NT_SMBS)
                rc = CIFSSMBOpen(xid, tcon, full_path, disposition,
-                        desiredAccess, CREATE_NOT_DIR, pnetfid, poplock, buf,
+                        desiredAccess, create_options, pnetfid, poplock, buf,
                         cifs_sb->local_nls, cifs_sb->mnt_cifs_flags
                                 & CIFS_MOUNT_MAP_SPECIAL_CHR);
        else
@@ -465,6 +469,7 @@ static int cifs_reopen_file(struct cifsFileInfo *pCifsFile, bool can_flush)
        char *full_path = NULL;
        int desiredAccess;
        int disposition = FILE_OPEN;
+       int create_options = CREATE_NOT_DIR;
        __u16 netfid;
 
        xid = GetXid();
@@ -524,6 +529,9 @@ static int cifs_reopen_file(struct cifsFileInfo *pCifsFile, bool can_flush)
 
        desiredAccess = cifs_convert_flags(pCifsFile->f_flags);
 
+       if (backup_cred(cifs_sb))
+               create_options |= CREATE_OPEN_BACKUP_INTENT;
+
        /* Can not refresh inode by passing in file_info buf to be returned
           by SMBOpen and then calling get_inode_info with returned buf
           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)
           that inode was not dirty locally we could do this */
 
        rc = CIFSSMBOpen(xid, tcon, full_path, disposition, desiredAccess,
-                        CREATE_NOT_DIR, &netfid, &oplock, NULL,
+                        create_options, &netfid, &oplock, NULL,
                         cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
                                CIFS_MOUNT_MAP_SPECIAL_CHR);
        if (rc) {
index db3f18cdf024382be5c30156952c5f95f264d24f..8693b5d0e180edaf11549d44a5fd9687aea3e077 100644 (file)
@@ -183,14 +183,20 @@ CIFSFormatMFSymlink(u8 *buf, unsigned int buf_len, const char *link_str)
 static int
 CIFSCreateMFSymLink(const int xid, struct cifs_tcon *tcon,
                    const char *fromName, const char *toName,
-                   const struct nls_table *nls_codepage, int remap)
+                   struct cifs_sb_info *cifs_sb)
 {
        int rc;
        int oplock = 0;
+       int remap;
+       int create_options = CREATE_NOT_DIR;
        __u16 netfid = 0;
        u8 *buf;
        unsigned int bytes_written = 0;
        struct cifs_io_parms io_parms;
+       struct nls_table *nls_codepage;
+
+       nls_codepage = cifs_sb->local_nls;
+       remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR;
 
        buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
        if (!buf)
@@ -202,8 +208,11 @@ CIFSCreateMFSymLink(const int xid, struct cifs_tcon *tcon,
                return rc;
        }
 
+       if (backup_cred(cifs_sb))
+               create_options |= CREATE_OPEN_BACKUP_INTENT;
+
        rc = CIFSSMBOpen(xid, tcon, fromName, FILE_CREATE, GENERIC_WRITE,
-                        CREATE_NOT_DIR, &netfid, &oplock, NULL,
+                        create_options, &netfid, &oplock, NULL,
                         nls_codepage, remap);
        if (rc != 0) {
                kfree(buf);
@@ -559,9 +568,7 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname)
        /* BB what if DFS and this volume is on different share? BB */
        if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS)
                rc = CIFSCreateMFSymLink(xid, pTcon, full_path, symname,
-                                        cifs_sb->local_nls,
-                                        cifs_sb->mnt_cifs_flags &
-                                               CIFS_MOUNT_MAP_SPECIAL_CHR);
+                                       cifs_sb);
        else if (pTcon->unix_ext)
                rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname,
                                           cifs_sb->local_nls);
index 4a1801b3195f3c0e24df249684cd5bd013c93900..703ef5c6fdb1e5716f63fe2d01cddce92c15f7af 100644 (file)
@@ -675,3 +675,18 @@ void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock)
                cinode->clientCanCacheRead = false;
        }
 }
+
+bool
+backup_cred(struct cifs_sb_info *cifs_sb)
+{
+       if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPUID) {
+               if (cifs_sb->mnt_backupuid == current_fsuid())
+                       return true;
+       }
+       if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPGID) {
+               if (in_group_p(cifs_sb->mnt_backupgid))
+                       return true;
+       }
+
+       return false;
+}