1
0
mirror of https://github.com/samba-team/samba.git synced 2025-03-09 08:58:35 +03:00

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>
This commit is contained in:
Tim Beale 2018-12-05 15:08:09 +13:00 committed by Tim Beale
parent 0af78faf5c
commit d7c7d6203b
2 changed files with 137 additions and 6 deletions

View File

@ -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')

View File

@ -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 }
};