diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 5d3e63aff253..3f7a18743ab3 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -1045,6 +1045,7 @@ const struct inode_operations cifs_dir_inode_ops = { .setattr = cifs_setattr, .symlink = cifs_symlink, .mknod = cifs_mknod, + .tmpfile = smb3_tmpfile, .listxattr = cifs_listxattr, }; diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index b59dc7478130..f3aa78bbf145 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -71,6 +71,7 @@ extern struct dentry *cifs_lookup(struct inode *, struct dentry *, extern int cifs_unlink(struct inode *dir, struct dentry *dentry); extern int cifs_hardlink(struct dentry *, struct inode *, struct dentry *); extern int cifs_mknod(struct inode *, struct dentry *, umode_t, dev_t); +extern int smb3_tmpfile(struct inode *, struct dentry *, umode_t); extern int cifs_mkdir(struct inode *, struct dentry *, umode_t); extern int cifs_rmdir(struct inode *, struct dentry *); extern int cifs_rename2(struct inode *, struct dentry *, struct inode *, diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index 79d842e7240c..a4e12e655e61 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -374,6 +374,7 @@ #define CREATE_OPTIONS_MASK 0x007FFFFF #define CREATE_OPTION_READONLY 0x10000000 #define CREATE_OPTION_SPECIAL 0x20000000 /* system. NB not sent over wire */ +#define CREATE_OPTION_TMP 0x40000000 /* set hidden/temporary as attrs */ /* ImpersonationLevel flags */ #define SECURITY_ANONYMOUS 0 diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index f3b79012ff29..6fd95c13cc2a 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -220,12 +220,15 @@ check_name(struct dentry *direntry, struct cifs_tcon *tcon) } +/* minimum supported name length in old systems would be 8.3 names, 12 bytes */ +#define SHORT_NAME_LEN 12 /* 8 + 1 (for dot) + 3 */ + /* Inode operations in similar order to how they appear in Linux file fs.h */ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid, struct tcon_link *tlink, unsigned oflags, umode_t mode, - __u32 *oplock, struct cifs_fid *fid) + __u32 *oplock, struct cifs_fid *fid, bool tmp) { int rc = -ENOENT; int create_options = CREATE_NOT_DIR; @@ -243,10 +246,21 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid, if (tcon->ses->server->oplocks) *oplock = REQ_OPLOCK; - full_path = build_path_from_dentry(direntry); + if (tmp) + full_path = kmalloc(SHORT_NAME_LEN + 1 /* for trailing null */, + GFP_KERNEL); + else + full_path = build_path_from_dentry(direntry); if (!full_path) return -ENOMEM; + /* fill in 8.3 (maximum size on some old file systems) random name */ + if (tmp) { + scnprintf(full_path, SHORT_NAME_LEN, "%lu", jiffies); + full_path[SHORT_NAME_LEN] = 0; /* trailing null */ + full_path[8] = '.'; /* 8.3 names have . as eighth character */ + } + if (tcon->unix_ext && cap_unix(tcon->ses) && !tcon->broken_posix_open && (CIFS_UNIX_POSIX_PATH_OPS_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability))) { @@ -321,6 +335,8 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid, desired_access |= GENERIC_READ; /* is this too little? */ if (OPEN_FMODE(oflags) & FMODE_WRITE) desired_access |= GENERIC_WRITE; + if (tmp) + desired_access |= DELETE; disposition = FILE_OVERWRITE_IF; if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) @@ -448,6 +464,7 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid, d_add(direntry, newinode); out: + cifs_dbg(VFS, "exiting cifs_do_create with rc %d\n",rc); /* BB REMOVEME */ kfree(buf); kfree(full_path); return rc; @@ -527,7 +544,7 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry, cifs_add_pending_open(&fid, tlink, &open); rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode, - &oplock, &fid); + &oplock, &fid, false); if (rc) { cifs_del_pending_open(&open); @@ -603,7 +620,47 @@ int cifs_create(struct inode *inode, struct dentry *direntry, umode_t mode, server->ops->new_lease_key(&fid); rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode, - &oplock, &fid); + &oplock, &fid, false); + if (!rc && server->ops->close) + server->ops->close(xid, tcon, &fid); + + + cifs_put_tlink(tlink); +out_free_xid: + free_xid(xid); + return rc; +} + +/* + * O_TMPFILE can be simulated over SMB3 using ATTR_TEMPORARY + */ +int smb3_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) +{ + int rc = -EOPNOTSUPP; + unsigned int xid = get_xid(); + unsigned oflags = O_EXCL | O_CREAT | O_RDWR; + struct tcon_link *tlink; + struct cifs_tcon *tcon; + struct TCP_Server_Info *server; + struct cifs_fid fid; + __u32 oplock; + + cifs_dbg(FYI, "create tmpfile - dentry = 0x%p with mode 0x%x\n", + dentry, mode); + + tlink = cifs_sb_tlink(CIFS_SB(dir->i_sb)); + rc = PTR_ERR(tlink); + if (IS_ERR(tlink)) + goto out_free_xid; + + tcon = tlink_tcon(tlink); + server = tcon->ses->server; + + if (server->ops->new_lease_key) + server->ops->new_lease_key(&fid); + + rc = cifs_do_create(dir, dentry, xid, tlink, oflags, mode, + &oplock, &fid, true); if (!rc && server->ops->close) server->ops->close(xid, tcon, &fid); diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 187a5ce68806..8d191da41973 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -2455,6 +2455,8 @@ SMB2_open_init(struct cifs_tcon *tcon, struct smb_rqst *rqst, __u8 *oplock, file_attributes |= ATTR_READONLY; if (oparms->create_options & CREATE_OPTION_SPECIAL) file_attributes |= ATTR_SYSTEM; + if (oparms->create_options & CREATE_OPTION_TMP) + file_attributes |= ATTR_TEMPORARY | ATTR_HIDDEN; req->ImpersonationLevel = IL_IMPERSONATION; req->DesiredAccess = cpu_to_le32(oparms->desired_access);