diff options
author | Tim Beale <timbeale@catalyst.net.nz> | 2018-12-05 15:08:09 +1300 |
---|---|---|
committer | Tim Beale <timbeale@samba.org> | 2019-01-07 01:23:08 +0100 |
commit | d7c7d6203ba85785d23481f3926aa6ddb29a7311 (patch) | |
tree | 0cadcf8bd77f8f4bf8ad236130033b11b278fb2b | |
parent | 0af78faf5c9424514ba5bc0baa18701c65c884e7 (diff) | |
download | samba-d7c7d6203ba85785d23481f3926aa6ddb29a7311.tar.gz |
s3:pylibsmb: Add .loadfile() API to SMB py bindings
Add a .loadfile API to read a file's contents. This provides a
convenient way to read a file and is consistent with the existing
source4 API, which is used by things like the GPO python code and the
ntacls backup.
BUG: https://bugzilla.samba.org/show_bug.cgi?id=13676
Signed-off-by: Tim Beale <timbeale@catalyst.net.nz>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
-rw-r--r-- | python/samba/tests/smb.py | 12 | ||||
-rw-r--r-- | source3/libsmb/pylibsmb.c | 131 |
2 files changed, 137 insertions, 6 deletions
diff --git a/python/samba/tests/smb.py b/python/samba/tests/smb.py index df590ffc5f8..bddadc5d165 100644 --- a/python/samba/tests/smb.py +++ b/python/samba/tests/smb.py @@ -150,7 +150,7 @@ class SMBTests(samba.tests.TestCase): def file_exists(self, filepath): """Returns whether a regular file exists (by trying to open it)""" try: - self.conn.loadfile(filepath) + self.smb_conn.loadfile(filepath) exists = True; except NTSTATUSError as err: if (err.args[0] == NT_STATUS_OBJECT_NAME_NOT_FOUND or @@ -197,14 +197,14 @@ class SMBTests(samba.tests.TestCase): self.smb_conn.savefile(test_file, test_contents.encode('utf8')) - contents = self.conn.loadfile(test_file) + contents = self.smb_conn.loadfile(test_file) self.assertEquals(contents.decode('utf8'), test_contents, msg='contents of test file did not match what was written') # check we can overwrite the file with new contents new_contents = 'wxyz' * 128 self.smb_conn.savefile(test_file, new_contents.encode('utf8')) - contents = self.conn.loadfile(test_file) + contents = self.smb_conn.loadfile(test_file) self.assertEquals(contents.decode('utf8'), new_contents, msg='contents of test file did not match what was written') @@ -213,7 +213,7 @@ class SMBTests(samba.tests.TestCase): def test_save_load_string_bytes(self): self.smb_conn.savefile(test_file, test_literal_bytes_embed_nulls) - contents = self.conn.loadfile(test_file) + contents = self.smb_conn.loadfile(test_file) self.assertEquals(contents, test_literal_bytes_embed_nulls, msg='contents of test file did not match what was written') @@ -222,7 +222,7 @@ class SMBTests(samba.tests.TestCase): if PY3: self.smb_conn.savefile(test_file, utf_contents.encode('utf8')) - contents = self.conn.loadfile(test_file) + contents = self.smb_conn.loadfile(test_file) self.assertEquals(contents.decode('utf8'), utf_contents, msg='contents of test file did not match what was written') @@ -231,7 +231,7 @@ class SMBTests(samba.tests.TestCase): def test_save_binary_contents(self): self.smb_conn.savefile(test_file, binary_contents) - contents = self.conn.loadfile(test_file) + contents = self.smb_conn.loadfile(test_file) self.assertEquals(contents, binary_contents, msg='contents of test file did not match what was written') diff --git a/source3/libsmb/pylibsmb.c b/source3/libsmb/pylibsmb.c index 2a1ae292035..2190b7c9c4a 100644 --- a/source3/libsmb/pylibsmb.c +++ b/source3/libsmb/pylibsmb.c @@ -28,6 +28,7 @@ #include "source4/libcli/util/pyerrors.h" #include "auth/credentials/pycredentials.h" #include "trans2.h" +#include "libsmb/clirap.h" static PyTypeObject *get_pytype(const char *module, const char *type) { @@ -863,6 +864,133 @@ static PyObject *py_cli_write(struct py_cli_state *self, PyObject *args, return Py_BuildValue("K", (unsigned long long)written); } +/* + * Returns the size of the given file + */ +static NTSTATUS py_smb_filesize(struct py_cli_state *self, uint16_t fnum, + off_t *size) +{ + NTSTATUS status; + + if (self->is_smb1) { + uint8_t *rdata = NULL; + struct tevent_req *req = NULL; + + req = cli_qfileinfo_send(NULL, self->ev, self->cli, fnum, + SMB_QUERY_FILE_ALL_INFO, 68, + CLI_BUFFER_SIZE); + if (!py_tevent_req_wait_exc(self, req)) { + return NT_STATUS_INTERNAL_ERROR; + } + status = cli_qfileinfo_recv(req, NULL, NULL, &rdata, NULL); + if (NT_STATUS_IS_OK(status)) { + *size = IVAL2_TO_SMB_BIG_UINT(rdata, 48); + } + TALLOC_FREE(req); + TALLOC_FREE(rdata); + } else { + status = cli_qfileinfo_basic(self->cli, fnum, NULL, size, + NULL, NULL, NULL, NULL, NULL); + } + return status; +} + +static NTSTATUS pull_helper(char *buf, size_t n, void *priv) +{ + char **dest_buf = (char **)priv; + memcpy(*dest_buf, buf, n); + *dest_buf += n; + return NT_STATUS_OK; +} + +/* + * Loads the specified file's contents and returns it + */ +static PyObject *py_smb_loadfile(struct py_cli_state *self, PyObject *args, + PyObject *kwargs) +{ + NTSTATUS status; + const char *filename = NULL; + struct tevent_req *req = NULL; + uint16_t fnum; + off_t size; + char *buf = NULL; + off_t nread = 0; + PyObject *result = NULL; + + if (!PyArg_ParseTuple(args, "s:loadfile", &filename)) { + return NULL; + } + + /* get a read file handle */ + req = cli_ntcreate_send(NULL, self->ev, self->cli, filename, 0, + FILE_READ_DATA, FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ, FILE_OPEN, 0, + SMB2_IMPERSONATION_IMPERSONATION, 0); + if (!py_tevent_req_wait_exc(self, req)) { + return NULL; + } + status = cli_ntcreate_recv(req, &fnum, NULL); + TALLOC_FREE(req); + PyErr_NTSTATUS_IS_ERR_RAISE(status); + + /* get a buffer to hold the file contents */ + status = py_smb_filesize(self, fnum, &size); + PyErr_NTSTATUS_IS_ERR_RAISE(status); + + result = PyBytes_FromStringAndSize(NULL, size); + if (result == NULL) { + return NULL; + } + + /* read the file contents */ + buf = PyBytes_AS_STRING(result); + req = cli_pull_send(NULL, self->ev, self->cli, fnum, 0, size, + size, pull_helper, &buf); + if (!py_tevent_req_wait_exc(self, req)) { + Py_XDECREF(result); + return NULL; + } + status = cli_pull_recv(req, &nread); + TALLOC_FREE(req); + if (!NT_STATUS_IS_OK(status)) { + Py_XDECREF(result); + PyErr_SetNTSTATUS(status); + return NULL; + } + + /* close the file handle */ + req = cli_close_send(NULL, self->ev, self->cli, fnum); + if (!py_tevent_req_wait_exc(self, req)) { + Py_XDECREF(result); + return NULL; + } + status = cli_close_recv(req); + TALLOC_FREE(req); + if (!NT_STATUS_IS_OK(status)) { + Py_XDECREF(result); + PyErr_SetNTSTATUS(status); + return NULL; + } + + /* sanity-check we read the expected number of bytes */ + if (nread > size) { + Py_XDECREF(result); + PyErr_Format(PyExc_IOError, + "read invalid - got %zu requested %zu", + nread, size); + return NULL; + } + + if (nread < size) { + if (_PyBytes_Resize(&result, nread) < 0) { + return NULL; + } + } + + return result; +} + static PyObject *py_cli_read(struct py_cli_state *self, PyObject *args, PyObject *kwds) { @@ -1303,6 +1431,9 @@ static PyMethodDef py_cli_state_methods[] = { { "savefile", (PyCFunction)py_smb_savefile, METH_VARARGS, "savefile(path, str) -> None\n\n" "\t\tWrite " PY_DESC_PY3_BYTES " str to file." }, + { "loadfile", (PyCFunction)py_smb_loadfile, METH_VARARGS, + "loadfile(path) -> file contents as a " PY_DESC_PY3_BYTES + "\n\n\t\tRead contents of a file." }, { NULL, NULL, 0, NULL } }; |