From: Pavel Shilovsky <[email protected]>
Signed-off-by: Pavel Shilovsky <[email protected]>
---
fs/cifs/smb2glob.h | 3 +-
fs/cifs/smb2inode.c | 10 +++-
fs/cifs/smb2link.c | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++
fs/cifs/smb2pdu.c | 2 +-
fs/cifs/smb2proto.h | 6 +++
5 files changed, 124 insertions(+), 5 deletions(-)
create mode 100644 fs/cifs/smb2link.c
diff --git a/fs/cifs/smb2glob.h b/fs/cifs/smb2glob.h
index 55d76ad..9e42f71 100644
--- a/fs/cifs/smb2glob.h
+++ b/fs/cifs/smb2glob.h
@@ -174,7 +174,7 @@ struct page_req {
#define SMB2_MAX_REQ 256
/* Identifiers for functions that use the open, operation, close pattern
- * in inode.c:open_op_close() */
+ * in smb2inode.c:smb2_open_op_close() */
#define SMB2_OP_SET_DELETE 1
#define SMB2_OP_SET_INFO 2
#define SMB2_OP_QUERY_INFO 3
@@ -182,6 +182,7 @@ struct page_req {
#define SMB2_OP_MKDIR 5
#define SMB2_OP_RENAME 6
#define SMB2_OP_DELETE 7
+#define SMB2_OP_HARDLINK 8
/* Used when constructing chained read requests. */
#define CHAINED_REQUEST 1
diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c
index f881055..8e69650 100644
--- a/fs/cifs/smb2inode.c
+++ b/fs/cifs/smb2inode.c
@@ -42,7 +42,7 @@ const struct inode_operations smb2_dir_inode_ops = {
.lookup = cifs_lookup,
.getattr = cifs_getattr,
.unlink = smb2_unlink,
- .link = cifs_hardlink,
+ .link = smb2_hardlink,
.mkdir = smb2_mkdir,
.rmdir = smb2_rmdir,
.rename = smb2_rename,
@@ -140,7 +140,7 @@ void smb2_set_ops(struct inode *inode)
}
}
-static int
+int
smb2_open_op_close(int xid, struct cifs_tcon *tcon, __le16 *path,
__u32 desired_access, __u32 create_disposition,
__u32 file_attributes, __u32 create_options,
@@ -149,7 +149,7 @@ smb2_open_op_close(int xid, struct cifs_tcon *tcon, __le16
*path,
int rc, tmprc = 0;
u64 persist_fid, volatile_fid;
- rc = SMB2_open(xid, tcon, srch_path, &persist_fid, &volatile_fid,
+ rc = SMB2_open(xid, tcon, path, &persist_fid, &volatile_fid,
desired_access, create_disposition, file_attributes,
create_options);
if (rc)
@@ -171,6 +171,10 @@ smb2_open_op_close(int xid, struct cifs_tcon *tcon, __le16
*path,
tmprc = SMB2_rename(xid, tcon, persist_fid, volatile_fid,
(__le16 *)data);
break;
+ case SMB2_OP_HARDLINK:
+ tmprc = SMB2_set_hardlink(xid, tcon, persist_fid, volatile_fid,
+ (__le16 *)data);
+ break;
default:
cERROR(1, "Invalid command");
break;
diff --git a/fs/cifs/smb2link.c b/fs/cifs/smb2link.c
new file mode 100644
index 0000000..8bac250
--- /dev/null
+++ b/fs/cifs/smb2link.c
@@ -0,0 +1,108 @@
+/*
+ * fs/cifs/smb2link.c
+ *
+ * Copyright (C) 2011
+ * Author(s): Pavel Shilovsky ([email protected]),
+ * Steve French ([email protected])
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/slab.h>
+#include <linux/namei.h>
+#include "cifsfs.h"
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+#include "cifs_fs_sb.h"
+#include "smb2glob.h"
+#include "smb2proto.h"
+
+int
+smb2_hardlink(struct dentry *old_file, struct inode *inode,
+ struct dentry *direntry)
+{
+ int rc = -EACCES;
+ int xid;
+ __le16 *from_name = NULL;
+ __le16 *to_name = NULL;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct tcon_link *tlink;
+ struct cifs_tcon *tcon;
+ struct cifsInodeInfo *cinode;
+
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink))
+ return PTR_ERR(tlink);
+ tcon = tlink_tcon(tlink);
+
+ xid = GetXid();
+
+ from_name = build_ucspath_from_dentry(old_file);
+ to_name = build_ucspath_from_dentry(direntry);
+ if ((from_name == NULL) || (to_name == NULL)) {
+ rc = -ENOMEM;
+ goto smb2_hl_exit;
+ }
+
+ rc = smb2_open_op_close(xid, tcon, from_name, FILE_READ_ATTRIBUTES,
+ FILE_OPEN, 0, 0, to_name, SMB2_OP_HARDLINK);
+
+ if ((rc == -EIO) || (rc == -EINVAL))
+ rc = -EOPNOTSUPP;
+
+ d_drop(direntry); /* force new lookup from server of target */
+
+ /*
+ * if source file is cached (oplocked) revalidate will not go to server
+ * until the file is closed or oplock broken so update nlinks locally.
+ */
+ if (old_file->d_inode) {
+ cinode = CIFS_I(old_file->d_inode);
+ if (rc == 0) {
+ old_file->d_inode->i_nlink++;
+/* BB should we make this contingent on superblock flag NOATIME? */
+/* old_file->d_inode->i_ctime = CURRENT_TIME;*/
+ /*
+ * parent dir timestamps will update from srv
+ * within a second, would it really be worth it
+ * to set the parent dir cifs inode time to zero
+ * to force revalidate (faster) for it too?
+ */
+ }
+ /*
+ * if not oplocked will force revalidate to get info
+ * on source file from srv.
+ */
+ cinode->time = 0;
+
+ /*
+ * Will update parent dir timestamps from srv within a second.
+ * Would it really be worth it to set the parent dir (cifs
+ * inode) time field to zero to force revalidate on parent
+ * directory faster ie
+ CIFS_I(inode)->time = 0;
+ */
+ }
+
+smb2_hl_exit:
+ kfree(from_name);
+ kfree(to_name);
+ FreeXid(xid);
+ cifs_put_tlink(tlink);
+ return rc;
+}
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index e5373da..308dd36 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -1965,7 +1965,7 @@ set_delete_exit:
}
int SMB2_set_hardlink(const int xid, struct cifs_tcon *tcon,
- u64 persistent_fid, u64 volatile_fid, __le16 *target_file)
+ u64 persistent_fid, u64 volatile_fid, __le16 *target_file)
{
struct set_info_req *pSMB2;
struct set_info_rsp *pSMB2r = NULL;
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
index 0ed7773..4e7337c 100644
--- a/fs/cifs/smb2proto.h
+++ b/fs/cifs/smb2proto.h
@@ -109,6 +109,10 @@ extern int smb2_query_file_info(struct file *filp);
extern int smb2_query_inode_info(struct inode **pinode, const char *full_path,
FILE_ALL_INFO *data, struct super_block *sb,
int xid);
+extern int smb2_open_op_close(int xid, struct cifs_tcon *tcon, __le16 *path,
+ __u32 desired_access, __u32 create_disposition,
+ __u32 file_attributes, __u32 create_options,
+ void *data, int command);
extern void smb2_set_ops(struct inode *inode);
extern bool smb2_is_size_safe_to_change(struct smb2_inode *smb2_ind,
__u64 end_of_file);
@@ -156,6 +160,8 @@ extern int smb2_rmdir(struct inode *inode, struct dentry
*direntry);
extern int smb2_unlink(struct inode *dir, struct dentry *dentry);
extern int smb2_rename(struct inode *source_dir, struct dentry *source_dentry,
struct inode *target_dir, struct dentry *target_dentry);
+extern int smb2_hardlink(struct dentry *old_file, struct inode *inode,
+ struct dentry *direntry);
extern int smb2_readdir(struct file *file, void *direntry, filldir_t filldir);
--
1.7.1
--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html