diff options
author | Lucian Petrut <lpetrut@cloudbasesolutions.com> | 2023-09-13 14:12:26 +0200 |
---|---|---|
committer | Lucian Petrut <lpetrut@cloudbasesolutions.com> | 2023-09-14 10:49:47 +0200 |
commit | 7d8db4954c2fedb4db9f0ccc8a9d304f35807b07 (patch) | |
tree | d2ac419d08605fd6766741d999a069af7dfdd4c1 /src/dokan | |
parent | Merge pull request #53408 from roysahar/nvmeof_fix_omap_state_object_contains... (diff) | |
download | ceph-7d8db4954c2fedb4db9f0ccc8a9d304f35807b07.tar.xz ceph-7d8db4954c2fedb4db9f0ccc8a9d304f35807b07.zip |
dokan: simple case insensitive emulation
While CephFS is case sensitive, Windows software commonly assume
that the filesystem is case insensitive.
In order to unblock certain use cases, a short term solution is to
simply normalize paths when creating or accessing files or
directories.
This change adds two ceph-dokan parameters:
* --case-insensitive: if set, paths are normalized
* --force-lowercase: normalized paths are converted to lowercase
instead of uppercase
This trivial implementation has some limitations:
* the original case is not preserved
* we could later on use xattr to store the original name
* can't access existing files that have a different case
* handled at ceph-dokan level
The advantage is that it's simple, shouldn't impact performance
and doesn't require libcephfs or mds changes.
In the future, we may conider a more robust implementation
at the mds and/or libcephfs level.
Signed-off-by: Lucian Petrut <lpetrut@cloudbasesolutions.com>
Diffstat (limited to 'src/dokan')
-rw-r--r-- | src/dokan/ceph_dokan.cc | 40 | ||||
-rw-r--r-- | src/dokan/ceph_dokan.h | 8 | ||||
-rw-r--r-- | src/dokan/options.cc | 9 |
3 files changed, 50 insertions, 7 deletions
diff --git a/src/dokan/ceph_dokan.cc b/src/dokan/ceph_dokan.cc index 9e115222cab..6459ea261bf 100644 --- a/src/dokan/ceph_dokan.cc +++ b/src/dokan/ceph_dokan.cc @@ -77,9 +77,26 @@ typedef struct { static_assert(sizeof(fd_context) <= 8, "fd_context exceeds DOKAN_FILE_INFO.Context size."); -string get_path(LPCWSTR path_w) { +string get_path(LPCWSTR path_w, bool normalize_case=true) { string path = to_string(path_w); replace(path.begin(), path.end(), '\\', '/'); + + if (normalize_case && !g_cfg->case_sensitive) { + if (g_cfg->convert_to_uppercase) { + std::transform( + path.begin(), path.end(), path.begin(), + [](unsigned char c){ + return std::toupper(c); + }); + } else { + std::transform( + path.begin(), path.end(), path.begin(), + [](unsigned char c){ + return std::tolower(c); + }); + } + } + return path; } @@ -543,6 +560,11 @@ static NTSTATUS WinCephFindFiles( return cephfs_errno_to_ntstatus_map(ret); } + // TODO: retrieve the original case (e.g. using xattr) if configured + // to do so. + // TODO: provide aliases when case insensitive mounts cause collisions. + // For example, when having test.txt and Test.txt, the latter becomes + // TEST~1.txt WIN32_FIND_DATAW findData; int count = 0; while (1) { @@ -794,14 +816,18 @@ static NTSTATUS WinCephGetVolumeInformation( { g_cfg->win_vol_name.copy(VolumeNameBuffer, VolumeNameSize); *VolumeSerialNumber = g_cfg->win_vol_serial; - *MaximumComponentLength = g_cfg->max_path_len; - *FileSystemFlags = FILE_CASE_SENSITIVE_SEARCH | - FILE_CASE_PRESERVED_NAMES | - FILE_SUPPORTS_REMOTE_STORAGE | - FILE_UNICODE_ON_DISK | - FILE_PERSISTENT_ACLS; + *FileSystemFlags = + FILE_SUPPORTS_REMOTE_STORAGE | + FILE_UNICODE_ON_DISK | + FILE_PERSISTENT_ACLS; + + if (g_cfg->case_sensitive) { + *FileSystemFlags |= + FILE_CASE_SENSITIVE_SEARCH | + FILE_CASE_PRESERVED_NAMES; + } wcscpy(FileSystemNameBuffer, L"Ceph"); return 0; diff --git a/src/dokan/ceph_dokan.h b/src/dokan/ceph_dokan.h index 5957d4dead1..fe48aa45814 100644 --- a/src/dokan/ceph_dokan.h +++ b/src/dokan/ceph_dokan.h @@ -36,6 +36,14 @@ struct Config { unsigned long max_path_len = 256; mode_t file_mode = 0755; mode_t dir_mode = 0755; + + bool case_sensitive = true; + // Convert new file paths to upper case in case of case insensitive mounts. + // Visual Studio recommends normalizing to uppercase in order to avoid + // locale issues (CA1308). + bool convert_to_uppercase = true; + // TODO: consider adding an option to preserve the original case. + // It could be stored using an extended attribute. }; extern Config *g_cfg; diff --git a/src/dokan/options.cc b/src/dokan/options.cc index 1ed90ef9d34..705e1117ca9 100644 --- a/src/dokan/options.cc +++ b/src/dokan/options.cc @@ -45,6 +45,11 @@ Map options: --max-path-len The value of the maximum path length. Default: 256. --file-mode The access mode to be used when creating files. --dir-mode The access mode to be used when creating directories. + --case-insensitive Emulate a case insensitive filesystem by normalizing + paths. The original case is NOT preserved. Existing + paths with a different case cannot be accessed. + --force-lowercase Use lowercase when normalizing paths. Uppercase is + used by default. Unmap options: -l [ --mountpoint ] arg mountpoint (path or drive letter) (e.g -l x). @@ -196,6 +201,10 @@ int parse_args( *err_msg << "ceph-dokan: Invalid argument for operation-timeout"; return -EINVAL; } + } else if (ceph_argparse_flag(args, i, "--case-insensitive", (char *)NULL)) { + cfg->case_sensitive = false; + } else if (ceph_argparse_flag(args, i, "--force-lowercase", (char *)NULL)) { + cfg->convert_to_uppercase = false; } else { ++i; } |