summaryrefslogtreecommitdiffstats
path: root/Modules
diff options
context:
space:
mode:
authorSteve Dower <steve.dower@python.org>2023-03-10 12:21:37 (GMT)
committerGitHub <noreply@github.com>2023-03-10 12:21:37 (GMT)
commitcb35882773a3ffc7fe0671e64848f4c926a2d52f (patch)
treed390ad1c69a25c999f6f9cf3d31de7b2323a8396 /Modules
parent2999e02836f9112de6b17784eaca762fb87e71a9 (diff)
downloadcpython-cb35882773a3ffc7fe0671e64848f4c926a2d52f.zip
cpython-cb35882773a3ffc7fe0671e64848f4c926a2d52f.tar.gz
cpython-cb35882773a3ffc7fe0671e64848f4c926a2d52f.tar.bz2
gh-102519: Add os.listdrives, os.listvolumes and os.listmounts on Windows (GH-102544)
Diffstat (limited to 'Modules')
-rw-r--r--Modules/clinic/posixmodule.c.h128
-rw-r--r--Modules/posixmodule.c194
2 files changed, 321 insertions, 1 deletions
diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h
index 6565f8d..8b0550d 100644
--- a/Modules/clinic/posixmodule.c.h
+++ b/Modules/clinic/posixmodule.c.h
@@ -1601,6 +1601,120 @@ exit:
#if defined(MS_WINDOWS)
+PyDoc_STRVAR(os_listdrives__doc__,
+"listdrives($module, /)\n"
+"--\n"
+"\n"
+"Return a list containing the names of drives in the system.\n"
+"\n"
+"A drive name typically looks like \'C:\\\\\'.");
+
+#define OS_LISTDRIVES_METHODDEF \
+ {"listdrives", (PyCFunction)os_listdrives, METH_NOARGS, os_listdrives__doc__},
+
+static PyObject *
+os_listdrives_impl(PyObject *module);
+
+static PyObject *
+os_listdrives(PyObject *module, PyObject *Py_UNUSED(ignored))
+{
+ return os_listdrives_impl(module);
+}
+
+#endif /* defined(MS_WINDOWS) */
+
+#if defined(MS_WINDOWS)
+
+PyDoc_STRVAR(os_listvolumes__doc__,
+"listvolumes($module, /)\n"
+"--\n"
+"\n"
+"Return a list containing the volumes in the system.\n"
+"\n"
+"Volumes are typically represented as a GUID path.");
+
+#define OS_LISTVOLUMES_METHODDEF \
+ {"listvolumes", (PyCFunction)os_listvolumes, METH_NOARGS, os_listvolumes__doc__},
+
+static PyObject *
+os_listvolumes_impl(PyObject *module);
+
+static PyObject *
+os_listvolumes(PyObject *module, PyObject *Py_UNUSED(ignored))
+{
+ return os_listvolumes_impl(module);
+}
+
+#endif /* defined(MS_WINDOWS) */
+
+#if defined(MS_WINDOWS)
+
+PyDoc_STRVAR(os_listmounts__doc__,
+"listmounts($module, /, volume)\n"
+"--\n"
+"\n"
+"Return a list containing mount points for a particular volume.\n"
+"\n"
+"\'volume\' should be a GUID path as returned from os.listvolumes.");
+
+#define OS_LISTMOUNTS_METHODDEF \
+ {"listmounts", _PyCFunction_CAST(os_listmounts), METH_FASTCALL|METH_KEYWORDS, os_listmounts__doc__},
+
+static PyObject *
+os_listmounts_impl(PyObject *module, path_t *volume);
+
+static PyObject *
+os_listmounts(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ PyObject *return_value = NULL;
+ #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
+
+ #define NUM_KEYWORDS 1
+ static struct {
+ PyGC_Head _this_is_not_used;
+ PyObject_VAR_HEAD
+ PyObject *ob_item[NUM_KEYWORDS];
+ } _kwtuple = {
+ .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
+ .ob_item = { &_Py_ID(volume), },
+ };
+ #undef NUM_KEYWORDS
+ #define KWTUPLE (&_kwtuple.ob_base.ob_base)
+
+ #else // !Py_BUILD_CORE
+ # define KWTUPLE NULL
+ #endif // !Py_BUILD_CORE
+
+ static const char * const _keywords[] = {"volume", NULL};
+ static _PyArg_Parser _parser = {
+ .keywords = _keywords,
+ .fname = "listmounts",
+ .kwtuple = KWTUPLE,
+ };
+ #undef KWTUPLE
+ PyObject *argsbuf[1];
+ path_t volume = PATH_T_INITIALIZE("listmounts", "volume", 0, 0);
+
+ args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf);
+ if (!args) {
+ goto exit;
+ }
+ if (!path_converter(args[0], &volume)) {
+ goto exit;
+ }
+ return_value = os_listmounts_impl(module, &volume);
+
+exit:
+ /* Cleanup for volume */
+ path_cleanup(&volume);
+
+ return return_value;
+}
+
+#endif /* defined(MS_WINDOWS) */
+
+#if defined(MS_WINDOWS)
+
PyDoc_STRVAR(os__getfullpathname__doc__,
"_getfullpathname($module, path, /)\n"
"--\n"
@@ -11253,6 +11367,18 @@ exit:
#define OS_LINK_METHODDEF
#endif /* !defined(OS_LINK_METHODDEF) */
+#ifndef OS_LISTDRIVES_METHODDEF
+ #define OS_LISTDRIVES_METHODDEF
+#endif /* !defined(OS_LISTDRIVES_METHODDEF) */
+
+#ifndef OS_LISTVOLUMES_METHODDEF
+ #define OS_LISTVOLUMES_METHODDEF
+#endif /* !defined(OS_LISTVOLUMES_METHODDEF) */
+
+#ifndef OS_LISTMOUNTS_METHODDEF
+ #define OS_LISTMOUNTS_METHODDEF
+#endif /* !defined(OS_LISTMOUNTS_METHODDEF) */
+
#ifndef OS__GETFULLPATHNAME_METHODDEF
#define OS__GETFULLPATHNAME_METHODDEF
#endif /* !defined(OS__GETFULLPATHNAME_METHODDEF) */
@@ -11796,4 +11922,4 @@ exit:
#ifndef OS_WAITSTATUS_TO_EXITCODE_METHODDEF
#define OS_WAITSTATUS_TO_EXITCODE_METHODDEF
#endif /* !defined(OS_WAITSTATUS_TO_EXITCODE_METHODDEF) */
-/*[clinic end generated code: output=9495478e51701b8a input=a9049054013a1b77]*/
+/*[clinic end generated code: output=47750e0e29c8d707 input=a9049054013a1b77]*/
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index 0d534f3..7d91f7e 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -4229,7 +4229,198 @@ os_listdir_impl(PyObject *module, path_t *path)
#endif
}
+
#ifdef MS_WINDOWS
+
+/*[clinic input]
+os.listdrives
+
+Return a list containing the names of drives in the system.
+
+A drive name typically looks like 'C:\\'.
+
+[clinic start generated code]*/
+
+static PyObject *
+os_listdrives_impl(PyObject *module)
+/*[clinic end generated code: output=aaece9dacdf682b5 input=1af9ccc9e583798e]*/
+{
+ /* Number of possible drives is limited, so 256 should always be enough.
+ On the day when it is not, listmounts() will have to be used. */
+ wchar_t buffer[256];
+ DWORD buflen = Py_ARRAY_LENGTH(buffer);
+ PyObject *result = NULL;
+ if (PySys_Audit("os.listdrives", NULL) < 0) {
+ return NULL;
+ }
+
+ Py_BEGIN_ALLOW_THREADS;
+ buflen = GetLogicalDriveStringsW(buflen, buffer);
+ Py_END_ALLOW_THREADS;
+
+ if (!buflen) {
+ PyErr_SetFromWindowsErr(0);
+ return NULL;
+ } else if (buflen >= Py_ARRAY_LENGTH(buffer)) {
+ PyErr_SetFromWindowsErr(ERROR_MORE_DATA);
+ return NULL;
+ }
+
+ /* buflen includes a null terminator, so remove it */
+ PyObject *str = PyUnicode_FromWideChar(buffer, buflen - 1);
+ if (str) {
+ PyObject *nullchar = PyUnicode_FromStringAndSize("\0", 1);
+ if (nullchar) {
+ result = PyUnicode_Split(str, nullchar, -1);
+ Py_DECREF(nullchar);
+ }
+ Py_DECREF(str);
+ }
+ return result;
+}
+
+/*[clinic input]
+os.listvolumes
+
+Return a list containing the volumes in the system.
+
+Volumes are typically represented as a GUID path.
+
+[clinic start generated code]*/
+
+static PyObject *
+os_listvolumes_impl(PyObject *module)
+/*[clinic end generated code: output=534e10ea2bf9d386 input=f6e4e70371f11e99]*/
+{
+ PyObject *result = PyList_New(0);
+ HANDLE find = INVALID_HANDLE_VALUE;
+ wchar_t buffer[MAX_PATH + 1];
+ if (!result) {
+ return NULL;
+ }
+ if (PySys_Audit("os.listvolumes", NULL) < 0) {
+ Py_DECREF(result);
+ return NULL;
+ }
+
+ int err = 0;
+ Py_BEGIN_ALLOW_THREADS;
+ find = FindFirstVolumeW(buffer, Py_ARRAY_LENGTH(buffer));
+ if (find == INVALID_HANDLE_VALUE) {
+ err = GetLastError();
+ }
+ Py_END_ALLOW_THREADS;
+
+ while (!err) {
+ PyObject *s = PyUnicode_FromWideChar(buffer, -1);
+ if (!s || PyList_Append(result, s) < 0) {
+ Py_XDECREF(s);
+ Py_CLEAR(result);
+ break;
+ }
+ Py_DECREF(s);
+
+ Py_BEGIN_ALLOW_THREADS;
+ if (!FindNextVolumeW(find, buffer, Py_ARRAY_LENGTH(buffer))) {
+ err = GetLastError();
+ }
+ Py_END_ALLOW_THREADS;
+ }
+
+ if (find != INVALID_HANDLE_VALUE) {
+ Py_BEGIN_ALLOW_THREADS;
+ FindVolumeClose(find);
+ Py_END_ALLOW_THREADS;
+ }
+ if (err && err != ERROR_NO_MORE_FILES) {
+ PyErr_SetFromWindowsErr(err);
+ Py_XDECREF(result);
+ result = NULL;
+ }
+ return result;
+}
+
+
+/*[clinic input]
+os.listmounts
+
+ volume: path_t
+
+Return a list containing mount points for a particular volume.
+
+'volume' should be a GUID path as returned from os.listvolumes.
+
+[clinic start generated code]*/
+
+static PyObject *
+os_listmounts_impl(PyObject *module, path_t *volume)
+/*[clinic end generated code: output=06da49679de4512e input=a8a27178e3f67845]*/
+{
+ wchar_t default_buffer[MAX_PATH + 1];
+ DWORD buflen = Py_ARRAY_LENGTH(default_buffer);
+ LPWSTR buffer = default_buffer;
+ DWORD attributes;
+ PyObject *str = NULL;
+ PyObject *nullchar = NULL;
+ PyObject *result = NULL;
+
+ /* Ensure we have a valid volume path before continuing */
+ Py_BEGIN_ALLOW_THREADS
+ attributes = GetFileAttributesW(volume->wide);
+ Py_END_ALLOW_THREADS
+ if (attributes == INVALID_FILE_ATTRIBUTES &&
+ GetLastError() == ERROR_UNRECOGNIZED_VOLUME)
+ {
+ return PyErr_SetFromWindowsErr(ERROR_UNRECOGNIZED_VOLUME);
+ }
+
+ if (PySys_Audit("os.listmounts", "O", volume->object) < 0) {
+ return NULL;
+ }
+
+ while (1) {
+ BOOL success;
+ Py_BEGIN_ALLOW_THREADS
+ success = GetVolumePathNamesForVolumeNameW(volume->wide, buffer,
+ buflen, &buflen);
+ Py_END_ALLOW_THREADS
+ if (success) {
+ break;
+ }
+ if (GetLastError() != ERROR_MORE_DATA) {
+ PyErr_SetFromWindowsErr(0);
+ goto exit;
+ }
+ if (buffer != default_buffer) {
+ PyMem_Free((void *)buffer);
+ }
+ buffer = (wchar_t*)PyMem_Malloc(sizeof(wchar_t) * buflen);
+ if (!buffer) {
+ PyErr_NoMemory();
+ goto exit;
+ }
+ }
+ if (buflen < 2) {
+ result = PyList_New(0);
+ goto exit;
+ }
+ // buflen includes two null terminators, one for the last string
+ // and one for the array of strings.
+ str = PyUnicode_FromWideChar(buffer, buflen - 2);
+ nullchar = PyUnicode_FromStringAndSize("\0", 1);
+ if (str && nullchar) {
+ result = PyUnicode_Split(str, nullchar, -1);
+ }
+exit:
+ if (buffer != default_buffer) {
+ PyMem_Free(buffer);
+ }
+ Py_XDECREF(nullchar);
+ Py_XDECREF(str);
+ return result;
+}
+
+
int
_PyOS_getfullpathname(const wchar_t *path, wchar_t **abspath_p)
{
@@ -15252,6 +15443,9 @@ static PyMethodDef posix_methods[] = {
OS_GETCWDB_METHODDEF
OS_LINK_METHODDEF
OS_LISTDIR_METHODDEF
+ OS_LISTDRIVES_METHODDEF
+ OS_LISTMOUNTS_METHODDEF
+ OS_LISTVOLUMES_METHODDEF
OS_LSTAT_METHODDEF
OS_MKDIR_METHODDEF
OS_NICE_METHODDEF