summaryrefslogtreecommitdiffstats
path: root/src/dokan
diff options
context:
space:
mode:
authorLucian Petrut <lpetrut@cloudbasesolutions.com>2023-09-13 14:12:26 +0200
committerLucian Petrut <lpetrut@cloudbasesolutions.com>2023-09-14 10:49:47 +0200
commit7d8db4954c2fedb4db9f0ccc8a9d304f35807b07 (patch)
treed2ac419d08605fd6766741d999a069af7dfdd4c1 /src/dokan
parentMerge pull request #53408 from roysahar/nvmeof_fix_omap_state_object_contains... (diff)
downloadceph-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.cc40
-rw-r--r--src/dokan/ceph_dokan.h8
-rw-r--r--src/dokan/options.cc9
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;
}