diff --git a/python/samba/tests/smb3unix.py b/python/samba/tests/smb3unix.py index 1d50df51387..7dc5cb3f348 100644 --- a/python/samba/tests/smb3unix.py +++ b/python/samba/tests/smb3unix.py @@ -19,6 +19,7 @@ from samba.samba3 import libsmb_samba_internal as libsmb from samba import NTSTATUSError,ntstatus import samba.tests.libsmb from samba.dcerpc import security +from samba.common import get_string def posix_context(mode): return (libsmb.SMB2_CREATE_TAG_POSIX, mode.to_bytes(4, 'little')) @@ -280,3 +281,58 @@ class Smb3UnixTests(samba.tests.libsmb.LibsmbTests): self.delete_test_file(c, '\\xx') self.disable_smb3unix() + + def test_posix_perm_files(self): + try: + self.enable_smb3unix() + + c = libsmb.Conn( + self.server_ip, + "smb3_posix_share", + self.lp, + self.creds, + posix=True) + self.assertTrue(c.have_posix()) + + test_files = {} + for perm in range(0o600, 0o7777+1): + # Owner write permission is required or cleanup will fail, and + # owner read is required to list the file if O_PATH is disabled + if perm & 0o600 != 0o600: + continue + + # Don't create with setuid or setgid. + if perm & 0o6000 != 0: + continue + + fname = 'testfile%04o' % perm + test_files[fname] = perm + f,_,cc_out = c.create_ex('\\%s' % fname, + DesiredAccess=security.SEC_STD_ALL, + CreateDisposition=libsmb.FILE_CREATE, + CreateContexts=[posix_context(perm)]) + c.close(f) + + dname = 'testdir%04o' % perm + test_files[dname] = perm + f,_,cc_out = c.create_ex('\\%s' % dname, + DesiredAccess=security.SEC_STD_ALL, + CreateDisposition=libsmb.FILE_CREATE, + CreateOptions=libsmb.FILE_DIRECTORY_FILE, + CreateContexts=[posix_context(perm)]) + c.close(f) + + res = c.list("", info_level=100, posix=True) + found_files = {get_string(i['name']): i['perms'] for i in res} + for fname, perm in test_files.items(): + self.assertIn(get_string(fname), found_files.keys(), + 'Test file not found') + self.assertEqual(test_files[fname], found_files[fname], + 'Requested %04o, Received %04o' % \ + (test_files[fname], found_files[fname])) + + finally: + for fname in test_files.keys(): + self.delete_test_file(c, '\\%s' % fname) + + self.disable_smb3unix() diff --git a/source3/libsmb/pylibsmb.c b/source3/libsmb/pylibsmb.c index e270c4a7f55..bb342bcd96f 100644 --- a/source3/libsmb/pylibsmb.c +++ b/source3/libsmb/pylibsmb.c @@ -1890,6 +1890,56 @@ static PyTypeObject py_cli_notify_state_type = { .tp_methods = py_cli_notify_state_methods, }; +/* + * Helper to add posix directory listing entries to an overall Python list + */ +static NTSTATUS list_posix_helper(struct file_info *finfo, + const char *mask, void *state) +{ + PyObject *result = (PyObject *)state; + PyObject *file = NULL; + PyObject *size = NULL; + int ret; + + /* suppress '.' and '..' in the results we return */ + if (ISDOT(finfo->name) || ISDOTDOT(finfo->name)) { + return NT_STATUS_OK; + } + size = PyLong_FromUnsignedLongLong(finfo->size); + /* + * Build a dictionary representing the file info. + * Note: Windows does not always return short_name (so it may be None) + */ + file = Py_BuildValue("{s:s,s:i,s:s,s:O,s:l,s:i,s:i,s:i,s:s,s:s}", + "name", finfo->name, + "attrib", (int)finfo->attr, + "short_name", finfo->short_name, + "size", size, + "mtime", + convert_timespec_to_time_t(finfo->mtime_ts), + "perms", finfo->st_ex_mode, + "ino", finfo->ino, + "dev", finfo->st_ex_dev, + "owner_sid", + dom_sid_string(finfo, &finfo->owner_sid), + "group_sid", + dom_sid_string(finfo, &finfo->group_sid)); + + Py_CLEAR(size); + + if (file == NULL) { + return NT_STATUS_NO_MEMORY; + } + + ret = PyList_Append(result, file); + Py_CLEAR(file); + if (ret == -1) { + return NT_STATUS_INTERNAL_ERROR; + } + + return NT_STATUS_OK; +} + /* * Helper to add directory listing entries to an overall Python list */ @@ -2022,6 +2072,8 @@ static PyObject *py_cli_list(struct py_cli_state *self, PyObject *result = NULL; const char *kwlist[] = { "directory", "mask", "attribs", "posix", "info_level", NULL }; + NTSTATUS (*callback_fn)(struct file_info *, const char *, void *) = + &list_helper; if (!ParseTupleAndKeywords(args, kwds, "z|sIpI:list", kwlist, &base_dir, &user_mask, &attribute, @@ -2042,8 +2094,11 @@ static PyObject *py_cli_list(struct py_cli_state *self, } } + if (posix) { + callback_fn = &list_posix_helper; + } status = do_listing(self, base_dir, user_mask, attribute, - info_level, posix, list_helper, result); + info_level, posix, callback_fn, result); if (!NT_STATUS_IS_OK(status)) { Py_XDECREF(result); @@ -2828,6 +2883,7 @@ MODULE_INIT_FUNC(libsmb_samba_cwrapper) ADD_FLAGS(FILE_OPEN_IF); ADD_FLAGS(FILE_OVERWRITE); ADD_FLAGS(FILE_OVERWRITE_IF); + ADD_FLAGS(FILE_DIRECTORY_FILE); return m; }