From: Christian Schoenebeck <qemu_oss@crudebyte.com>
To: qemu-devel@nongnu.org
Cc: Will Cohen <wwcohen@gmail.com>, Greg Kurz <groug@kaod.org>,
Michael Roitzsch <reactorcontrol@icloud.com>,
Keno Fischer <keno@juliacomputing.com>,
Dominique Martinet <asmadeus@codewreck.org>,
Eric Van Hensbergen <ericvh@gmail.com>,
linux-kernel@vger.kernel.org, netdev@vger.kernel.org,
v9fs-developer@lists.sourceforge.net,
Latchesar Ionkov <lucho@ionkov.net>
Subject: [RFC PATCH] 9p: case-insensitive host filesystems
Date: Fri, 22 Apr 2022 20:02:46 +0200 [thread overview]
Message-ID: <1757498.AyhHxzoH2B@silver> (raw)
Now that 9p support for macOS hosts just landed in QEMU 7.0 and with support
for Windows hosts on the horizon [1], the question is how to deal with case-
insensitive host filesystems, which are very common on those two systems?
I made some tests, e.g. trying to setup a 9p root fs Linux installation on a
macOS host as described in the QEMU HOWTO [2], which at a certain point causes
the debootstrap script to fail when trying to unpack the 'libpam-runtime'
package. That's because it would try to create this symlink:
/usr/share/man/man7/PAM.7.gz -> /usr/share/man/man7/pam.7.gz
which fails with EEXIST on a case-insensitive APFS. Unfortunately you can't
easily switch an existing APFS partition to case-sensitivity. It requires to
reformat the entire partition, loosing all your data, etc.
So I did a quick test with QEMU as outlined below, trying to simply let 9p
server "eat" EEXIST errors in such cases, but then I realized that most of the
time it would not even come that far, as Linux client would first send a
'Twalk' request to check whether target symlink entry already exists, and as
it gets a positive response from 9p server (again, due to case-insensitivity)
client would stop right there without even trying to send a 'Tsymlink'
request.
So maybe it's better to handle case-insensitivity entirely on client side?
I've read that some generic "case fold" code has landed in the Linux kernel
recently that might do the trick?
Should 9p server give a hint to 9p client that it's a case-insensitive fs? And
if yes, once per entire exported fs or rather for each directory (as there
might be submounts on host)?
[1] https://lore.kernel.org/all/20220408171013.912436-1-bmeng.cn@gmail.com/
[2] https://wiki.qemu.org/Documentation/9p_root_fs
---
hw/9pfs/9p-local.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 54 insertions(+)
diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c
index d42ce6d8b8..d6cb45c758 100644
--- a/hw/9pfs/9p-local.c
+++ b/hw/9pfs/9p-local.c
@@ -39,6 +39,10 @@
#endif
#endif
#include <sys/ioctl.h>
+#ifdef CONFIG_DARWIN
+#include <glib.h>
+#include <glib/gprintf.h>
+#endif
#ifndef XFS_SUPER_MAGIC
#define XFS_SUPER_MAGIC 0x58465342
@@ -57,6 +61,18 @@ typedef struct {
int mountfd;
} LocalData;
+#ifdef CONFIG_DARWIN
+
+/* Compare strings case-insensitive (assuming UTF-8 encoding). */
+static int p9_stricmp(const char *a, const char *b)
+{
+ g_autofree gchar *cia = g_utf8_casefold(a, -1);
+ g_autofree gchar *cib = g_utf8_casefold(b, -1);
+ return g_utf8_collate(cia, cib);
+}
+
+#endif
+
int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags,
mode_t mode)
{
@@ -931,6 +947,25 @@ static int local_symlink(FsContext *fs_ctx, const char
*oldpath,
fs_ctx->export_flags & V9FS_SM_NONE) {
err = symlinkat(oldpath, dirfd, name);
if (err) {
+#if CONFIG_DARWIN
+ if (errno == EEXIST) {
+ printf(" -> symlinkat(oldpath='%s', dirfd=%d, name='%s') =
EEXIST\n", oldpath, dirfd, name);
+ }
+ if (errno == EEXIST &&
+ strcmp(oldpath, name) && !p9_stricmp(oldpath, name))
+ {
+ struct stat st1, st2;
+ const int cur_errno = errno;
+ if (!fstatat(dirfd, oldpath, &st1, AT_SYMLINK_NOFOLLOW) &&
+ !fstatat(dirfd, name, &st2, AT_SYMLINK_NOFOLLOW) &&
+ st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino)
+ {
+ printf(" -> iCASE SAME\n");
+ err = 0;
+ }
+ errno = cur_errno;
+ }
+#endif
goto out;
}
err = fchownat(dirfd, name, credp->fc_uid, credp->fc_gid,
@@ -983,6 +1018,25 @@ static int local_link(FsContext *ctx, V9fsPath *oldpath,
ret = linkat(odirfd, oname, ndirfd, name, 0);
if (ret < 0) {
+#if CONFIG_DARWIN
+ if (errno == EEXIST) {
+ printf(" -> linkat(odirfd=%d, oname='%s', ndirfd=%d, name='%s')
= EEXIST\n", odirfd, oname, ndirfd, name);
+ }
+ if (errno == EEXIST &&
+ strcmp(oname, name) && !p9_stricmp(oname, name))
+ {
+ struct stat st1, st2;
+ const int cur_errno = errno;
+ if (!fstatat(odirfd, oname, &st1, AT_SYMLINK_NOFOLLOW) &&
+ !fstatat(ndirfd, name, &st2, AT_SYMLINK_NOFOLLOW) &&
+ st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino)
+ {
+ printf(" -> iCASE SAME\n");
+ ret = 0;
+ }
+ errno = cur_errno;
+ }
+#endif
goto out_close;
}
--
2.32.0 (Apple Git-132)
next reply other threads:[~2022-04-22 22:22 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-04-22 18:02 Christian Schoenebeck [this message]
2022-04-22 19:57 ` [RFC PATCH] 9p: case-insensitive host filesystems Dominique Martinet
2022-04-22 19:57 ` Dominique Martinet
2022-05-23 17:59 ` Christian Schoenebeck
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1757498.AyhHxzoH2B@silver \
--to=qemu_oss@crudebyte.com \
--cc=asmadeus@codewreck.org \
--cc=ericvh@gmail.com \
--cc=groug@kaod.org \
--cc=keno@juliacomputing.com \
--cc=linux-kernel@vger.kernel.org \
--cc=lucho@ionkov.net \
--cc=netdev@vger.kernel.org \
--cc=qemu-devel@nongnu.org \
--cc=reactorcontrol@icloud.com \
--cc=v9fs-developer@lists.sourceforge.net \
--cc=wwcohen@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.