summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorPavel Shilovsky <pshilovsky@samba.org>2012-09-18 16:20:34 -0700
committerSteve French <smfrench@gmail.com>2012-09-24 21:46:30 -0500
commit6fc05c25ca35e65ee1759dd803f23576a268f5ec (patch)
tree048a8fa4bf1e8c0b486d8b037f0b7eaaf0b4c755 /fs
parent76ec5e33846de386f44826f145cd725b92c23630 (diff)
downloadlinux-6fc05c25ca35e65ee1759dd803f23576a268f5ec.tar.bz2
CIFS: Add statfs support for SMB2
Signed-off-by: Pavel Shilovsky <pshilovsky@samba.org> Signed-off-by: Steve French <smfrench@gmail.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/cifs/smb2glob.h2
-rw-r--r--fs/cifs/smb2ops.c22
-rw-r--r--fs/cifs/smb2pdu.c81
-rw-r--r--fs/cifs/smb2pdu.h19
-rw-r--r--fs/cifs/smb2proto.h3
5 files changed, 127 insertions, 0 deletions
diff --git a/fs/cifs/smb2glob.h b/fs/cifs/smb2glob.h
index 05d429b1c37e..7c0e2143e775 100644
--- a/fs/cifs/smb2glob.h
+++ b/fs/cifs/smb2glob.h
@@ -23,6 +23,8 @@
#ifndef _SMB2_GLOB_H
#define _SMB2_GLOB_H
+#define SMB2_MAGIC_NUMBER 0xFE534D42
+
/*
*****************************************************************
* Constants go here
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index 8c72e0768d12..3a8c68258026 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -18,12 +18,14 @@
*/
#include <linux/pagemap.h>
+#include <linux/vfs.h>
#include "cifsglob.h"
#include "smb2pdu.h"
#include "smb2proto.h"
#include "cifsproto.h"
#include "cifs_debug.h"
#include "smb2status.h"
+#include "smb2glob.h"
static int
change_conf(struct TCP_Server_Info *server)
@@ -522,6 +524,25 @@ smb2_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid,
cinode->clientCanCacheRead ? 1 : 0);
}
+static int
+smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
+ struct kstatfs *buf)
+{
+ int rc;
+ u64 persistent_fid, volatile_fid;
+ __le16 srch_path = 0; /* Null - open root of share */
+ u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
+
+ rc = SMB2_open(xid, tcon, &srch_path, &persistent_fid, &volatile_fid,
+ FILE_READ_ATTRIBUTES, FILE_OPEN, 0, 0, &oplock, NULL);
+ if (rc)
+ return rc;
+ buf->f_type = SMB2_MAGIC_NUMBER;
+ rc = SMB2_QFS_info(xid, tcon, persistent_fid, volatile_fid, buf);
+ SMB2_close(xid, tcon, persistent_fid, volatile_fid);
+ return rc;
+}
+
struct smb_version_operations smb21_operations = {
.setup_request = smb2_setup_request,
.setup_async_request = smb2_setup_async_request,
@@ -578,6 +599,7 @@ struct smb_version_operations smb21_operations = {
.calc_smb_size = smb2_calc_size,
.is_status_pending = smb2_is_status_pending,
.oplock_response = smb2_oplock_response,
+ .queryfs = smb2_queryfs,
};
struct smb_version_values smb21_values = {
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 566d86b0ad96..994c184ac9a9 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -1971,3 +1971,84 @@ SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
return rc;
}
+
+static void
+copy_fs_info_to_kstatfs(struct smb2_fs_full_size_info *pfs_inf,
+ struct kstatfs *kst)
+{
+ kst->f_bsize = le32_to_cpu(pfs_inf->BytesPerSector) *
+ le32_to_cpu(pfs_inf->SectorsPerAllocationUnit);
+ kst->f_blocks = le64_to_cpu(pfs_inf->TotalAllocationUnits);
+ kst->f_bfree = le64_to_cpu(pfs_inf->ActualAvailableAllocationUnits);
+ kst->f_bavail = le64_to_cpu(pfs_inf->CallerAvailableAllocationUnits);
+ return;
+}
+
+static int
+build_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon, int level,
+ int outbuf_len, u64 persistent_fid, u64 volatile_fid)
+{
+ int rc;
+ struct smb2_query_info_req *req;
+
+ cFYI(1, "Query FSInfo level %d", level);
+
+ if ((tcon->ses == NULL) || (tcon->ses->server == NULL))
+ return -EIO;
+
+ rc = small_smb2_init(SMB2_QUERY_INFO, tcon, (void **) &req);
+ if (rc)
+ return rc;
+
+ req->InfoType = SMB2_O_INFO_FILESYSTEM;
+ req->FileInfoClass = level;
+ req->PersistentFileId = persistent_fid;
+ req->VolatileFileId = volatile_fid;
+ /* 4 for rfc1002 length field and 1 for pad */
+ req->InputBufferOffset =
+ cpu_to_le16(sizeof(struct smb2_query_info_req) - 1 - 4);
+ req->OutputBufferLength = cpu_to_le32(
+ outbuf_len + sizeof(struct smb2_query_info_rsp) - 1 - 4);
+
+ iov->iov_base = (char *)req;
+ /* 4 for rfc1002 length field */
+ iov->iov_len = get_rfc1002_length(req) + 4;
+ return 0;
+}
+
+int
+SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata)
+{
+ struct smb2_query_info_rsp *rsp = NULL;
+ struct kvec iov;
+ int rc = 0;
+ int resp_buftype;
+ struct cifs_ses *ses = tcon->ses;
+ struct smb2_fs_full_size_info *info = NULL;
+
+ rc = build_qfs_info_req(&iov, tcon, FS_FULL_SIZE_INFORMATION,
+ sizeof(struct smb2_fs_full_size_info),
+ persistent_fid, volatile_fid);
+ if (rc)
+ return rc;
+
+ rc = SendReceive2(xid, ses, &iov, 1, &resp_buftype, 0);
+ if (rc) {
+ cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE);
+ goto qinf_exit;
+ }
+ rsp = (struct smb2_query_info_rsp *)iov.iov_base;
+
+ info = (struct smb2_fs_full_size_info *)(4 /* RFC1001 len */ +
+ le16_to_cpu(rsp->OutputBufferOffset) + (char *)&rsp->hdr);
+ rc = validate_buf(le16_to_cpu(rsp->OutputBufferOffset),
+ le32_to_cpu(rsp->OutputBufferLength), &rsp->hdr,
+ sizeof(struct smb2_fs_full_size_info));
+ if (!rc)
+ copy_fs_info_to_kstatfs(info, fsdata);
+
+qinf_exit:
+ free_rsp_buf(resp_buftype, iov.iov_base);
+ return rc;
+}
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
index 8a0745764297..3c8e99ea88ad 100644
--- a/fs/cifs/smb2pdu.h
+++ b/fs/cifs/smb2pdu.h
@@ -630,6 +630,25 @@ struct smb2_oplock_break {
* BB consider moving to a different header
*/
+/* File System Information Classes */
+#define FS_VOLUME_INFORMATION 1 /* Query */
+#define FS_LABEL_INFORMATION 2 /* Set */
+#define FS_SIZE_INFORMATION 3 /* Query */
+#define FS_DEVICE_INFORMATION 4 /* Query */
+#define FS_ATTRIBUTE_INFORMATION 5 /* Query */
+#define FS_CONTROL_INFORMATION 6 /* Query, Set */
+#define FS_FULL_SIZE_INFORMATION 7 /* Query */
+#define FS_OBJECT_ID_INFORMATION 8 /* Query, Set */
+#define FS_DRIVER_PATH_INFORMATION 9 /* Query */
+
+struct smb2_fs_full_size_info {
+ __le64 TotalAllocationUnits;
+ __le64 CallerAvailableAllocationUnits;
+ __le64 ActualAvailableAllocationUnits;
+ __le32 SectorsPerAllocationUnit;
+ __le32 BytesPerSector;
+} __packed;
+
/* partial list of QUERY INFO levels */
#define FILE_DIRECTORY_INFORMATION 1
#define FILE_FULL_DIRECTORY_INFORMATION 2
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
index de554b716fdf..a73a963af8f4 100644
--- a/fs/cifs/smb2proto.h
+++ b/fs/cifs/smb2proto.h
@@ -138,5 +138,8 @@ extern int SMB2_set_info(const unsigned int xid, struct cifs_tcon *tcon,
extern int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
const u64 persistent_fid, const u64 volatile_fid,
const __u8 oplock_level);
+extern int SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_file_id, u64 volatile_file_id,
+ struct kstatfs *FSData);
#endif /* _SMB2PROTO_H */