lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <158215745745.386537.12978619503606431141.stgit@warthog.procyon.org.uk>
Date:   Thu, 20 Feb 2020 00:10:57 +0000
From:   David Howells <dhowells@...hat.com>
To:     viro@...iv.linux.org.uk
Cc:     torvalds@...ux-foundation.org, dhowells@...hat.com,
        linux-afs@...ts.infradead.org, linux-kernel@...r.kernel.org
Subject: [RFC PATCH] afs: Create a mountpoint through symlink() and remove
 through rmdir()

If symlink() is given a magic prefix in the target string, turn it into a
mountpoint instead.

The prefix is "//_afs3_mount:".  POSIX says that a double slash at the
beginning of a filename is special:

    A pathname consisting of a single slash shall resolve to the root
    directory of the process.  A null pathname shall not be successfully
    resolved.  A pathname that begins with two successive slashes may be
    interpreted in an implementation-defined manner, although more than two
    leading slashes shall be treated as a single slash.

however, that might be validly interpreted by Windows as a UNC name.  So
the prefix is made a bit more than that to make that less likely.  The
prefix is then stripped off and a "." is added for transmission to the
server.

afs_mntpt_lookup() is removed so that the new dentry is marked as being an
autodir type.  afs_rmdir() then checks for this and switches over to
afs_unlink() to remove a mountpoint (as far as the server is concerned,
it's not a directory).  The unlink() system call can't be used to remove
the mountpoint without alteration to the core VFS as may_delete() throws an
error.  This allows rmdir() to remove an automount point (provided it's not
mounted over).

Note that this behaviour varies from other AFS implementations in that the
proposed symlink *could* be valid content (though it seems unlikely) and in
those implementations rmdir can't remove a mountpoint.  Since the Linux
symlink() system can handle a target string of 4095 characters, but AFS can
only handle a symlink of 1024 chars, it might be better to make the magic
prefix large enough to force the target string length over the server
limit.

Signed-off-by: David Howells <dhowells@...hat.com>
---

 fs/afs/dir.c       |   38 +++++++++++++++++++++++++++++++++++---
 fs/afs/fsclient.c  |    9 +++++----
 fs/afs/internal.h  |    6 ++++--
 fs/afs/mntpt.c     |   15 ---------------
 fs/afs/yfsclient.c |    7 ++++---
 5 files changed, 48 insertions(+), 27 deletions(-)

diff --git a/fs/afs/dir.c b/fs/afs/dir.c
index 5c794f4b051a..40552036cd6a 100644
--- a/fs/afs/dir.c
+++ b/fs/afs/dir.c
@@ -1365,6 +1365,9 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
 	_enter("{%llx:%llu},{%pd}",
 	       dvnode->fid.vid, dvnode->fid.vnode, dentry);
 
+	if (d_is_autodir(dentry))
+		return afs_unlink(dir, dentry);
+
 	scb = kzalloc(sizeof(struct afs_status_cb), GFP_KERNEL);
 	if (!scb)
 		return -ENOMEM;
@@ -1721,17 +1724,21 @@ static int afs_link(struct dentry *from, struct inode *dir,
 	return ret;
 }
 
+static const char afs_mount_prefix[] = "//_afs3_mount:";
+
 /*
- * create a symlink in an AFS filesystem
+ * create a symlink or a mountpoint in an AFS filesystem
  */
 static int afs_symlink(struct inode *dir, struct dentry *dentry,
-		       const char *content)
+		       const char *specified_content)
 {
 	struct afs_iget_data iget_data;
 	struct afs_fs_cursor fc;
 	struct afs_status_cb *scb;
 	struct afs_vnode *dvnode = AFS_FS_I(dir);
 	struct key *key;
+	char *content = (char *)specified_content;
+	bool is_mountpoint = false;
 	int ret;
 
 	_enter("{%llx:%llu},{%pd},%s",
@@ -1746,6 +1753,28 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry,
 	if (strlen(content) >= AFSPATHMAX)
 		goto error;
 
+	if (memcmp(content, afs_mount_prefix,
+		   sizeof(afs_mount_prefix) - 1) == 0) {
+		/* This is going to be a mountpoint. */
+		char *p = content + sizeof(afs_mount_prefix) - 1, *c;
+		size_t clen = strlen(p);
+
+		if (clen < 2 ||
+		    (p[0] != '%' && p[0] != '#'))
+			goto error;
+
+		/* Snip off the prefix and append a dot */
+		ret = -ENOMEM;
+		c = kmalloc(clen + 2, GFP_KERNEL);
+		if (!c)
+			goto error;
+		memcpy(c, p, clen);
+		c[clen] = '.';
+		c[clen + 1] = 0;
+		content = c;
+		is_mountpoint = true;
+	}
+
 	ret = -ENOMEM;
 	scb = kcalloc(2, sizeof(struct afs_status_cb), GFP_KERNEL);
 	if (!scb)
@@ -1765,7 +1794,8 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry,
 			fc.cb_break = afs_calc_vnode_cb_break(dvnode);
 			afs_prep_for_new_inode(&fc, &iget_data);
 			afs_fs_symlink(&fc, dentry->d_name.name, content,
-				       &scb[0], &iget_data.fid, &scb[1]);
+				       &scb[0], &iget_data.fid, &scb[1],
+				       is_mountpoint);
 		}
 
 		afs_check_for_remote_deletion(&fc, dvnode);
@@ -1794,6 +1824,8 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry,
 error_scb:
 	kfree(scb);
 error:
+	if (content != specified_content)
+		kfree(content);
 	d_drop(dentry);
 	_leave(" = %d", ret);
 	return ret;
diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c
index 1f9c5d8e6fe5..e2a2abe3a9aa 100644
--- a/fs/afs/fsclient.c
+++ b/fs/afs/fsclient.c
@@ -896,14 +896,15 @@ static const struct afs_call_type afs_RXFSSymlink = {
 };
 
 /*
- * create a symbolic link
+ * Create a symbolic link or a mountpoint (differentiated by mode).
  */
 int afs_fs_symlink(struct afs_fs_cursor *fc,
 		   const char *name,
 		   const char *contents,
 		   struct afs_status_cb *dvnode_scb,
 		   struct afs_fid *newfid,
-		   struct afs_status_cb *new_scb)
+		   struct afs_status_cb *new_scb,
+		   bool is_mountpoint)
 {
 	struct afs_vnode *dvnode = fc->vnode;
 	struct afs_call *call;
@@ -913,7 +914,7 @@ int afs_fs_symlink(struct afs_fs_cursor *fc,
 
 	if (test_bit(AFS_SERVER_FL_IS_YFS, &fc->cbi->server->flags))
 		return yfs_fs_symlink(fc, name, contents, dvnode_scb,
-				      newfid, new_scb);
+				      newfid, new_scb, is_mountpoint);
 
 	_enter("");
 
@@ -959,7 +960,7 @@ int afs_fs_symlink(struct afs_fs_cursor *fc,
 	*bp++ = htonl(dvnode->vfs_inode.i_mtime.tv_sec); /* mtime */
 	*bp++ = 0; /* owner */
 	*bp++ = 0; /* group */
-	*bp++ = htonl(S_IRWXUGO); /* unix mode */
+	*bp++ = htonl(is_mountpoint ? 0644 : S_IRWXUGO); /* unix mode */
 	*bp++ = 0; /* segment size */
 
 	afs_use_fs_server(call, fc->cbi);
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index 1d81fc4c3058..70509f2ddd00 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -965,7 +965,8 @@ extern int afs_fs_remove(struct afs_fs_cursor *, struct afs_vnode *, const char
 extern int afs_fs_link(struct afs_fs_cursor *, struct afs_vnode *, const char *,
 		       struct afs_status_cb *, struct afs_status_cb *);
 extern int afs_fs_symlink(struct afs_fs_cursor *, const char *, const char *,
-			  struct afs_status_cb *, struct afs_fid *, struct afs_status_cb *);
+			  struct afs_status_cb *, struct afs_fid *, struct afs_status_cb *,
+			  bool);
 extern int afs_fs_rename(struct afs_fs_cursor *, const char *,
 			 struct afs_vnode *, const char *,
 			 struct afs_status_cb *, struct afs_status_cb *);
@@ -1370,7 +1371,8 @@ extern int yfs_fs_remove(struct afs_fs_cursor *, struct afs_vnode *, const char
 extern int yfs_fs_link(struct afs_fs_cursor *, struct afs_vnode *, const char *,
 		       struct afs_status_cb *, struct afs_status_cb *);
 extern int yfs_fs_symlink(struct afs_fs_cursor *, const char *, const char *,
-			  struct afs_status_cb *, struct afs_fid *, struct afs_status_cb *);
+			  struct afs_status_cb *, struct afs_fid *, struct afs_status_cb *,
+			  bool);
 extern int yfs_fs_rename(struct afs_fs_cursor *, const char *, struct afs_vnode *, const char *,
 			 struct afs_status_cb *, struct afs_status_cb *);
 extern int yfs_fs_store_data(struct afs_fs_cursor *, struct address_space *,
diff --git a/fs/afs/mntpt.c b/fs/afs/mntpt.c
index 79bc5f1338ed..b06ceed9b8f5 100644
--- a/fs/afs/mntpt.c
+++ b/fs/afs/mntpt.c
@@ -17,9 +17,6 @@
 #include "internal.h"
 
 
-static struct dentry *afs_mntpt_lookup(struct inode *dir,
-				       struct dentry *dentry,
-				       unsigned int flags);
 static int afs_mntpt_open(struct inode *inode, struct file *file);
 static void afs_mntpt_expiry_timed_out(struct work_struct *work);
 
@@ -29,7 +26,6 @@ const struct file_operations afs_mntpt_file_operations = {
 };
 
 const struct inode_operations afs_mntpt_inode_operations = {
-	.lookup		= afs_mntpt_lookup,
 	.readlink	= page_readlink,
 	.getattr	= afs_getattr,
 	.listxattr	= afs_listxattr,
@@ -46,17 +42,6 @@ static unsigned long afs_mntpt_expiry_timeout = 10 * 60;
 
 static const char afs_root_volume[] = "root.cell";
 
-/*
- * no valid lookup procedure on this sort of dir
- */
-static struct dentry *afs_mntpt_lookup(struct inode *dir,
-				       struct dentry *dentry,
-				       unsigned int flags)
-{
-	_enter("%p,%p{%pd2}", dir, dentry, dentry);
-	return ERR_PTR(-EREMOTE);
-}
-
 /*
  * no valid open procedure on this sort of dir
  */
diff --git a/fs/afs/yfsclient.c b/fs/afs/yfsclient.c
index a26126ac7bf1..5ff95d5643f5 100644
--- a/fs/afs/yfsclient.c
+++ b/fs/afs/yfsclient.c
@@ -1080,14 +1080,15 @@ static const struct afs_call_type yfs_RXYFSSymlink = {
 };
 
 /*
- * Create a symbolic link.
+ * Create a symbolic link or a mountpoint (differentiated by mode).
  */
 int yfs_fs_symlink(struct afs_fs_cursor *fc,
 		   const char *name,
 		   const char *contents,
 		   struct afs_status_cb *dvnode_scb,
 		   struct afs_fid *newfid,
-		   struct afs_status_cb *vnode_scb)
+		   struct afs_status_cb *vnode_scb,
+		   bool is_mountpoint)
 {
 	struct afs_vnode *dvnode = fc->vnode;
 	struct afs_call *call;
@@ -1125,7 +1126,7 @@ int yfs_fs_symlink(struct afs_fs_cursor *fc,
 	bp = xdr_encode_YFSFid(bp, &dvnode->fid);
 	bp = xdr_encode_string(bp, name, namesz);
 	bp = xdr_encode_string(bp, contents, contents_sz);
-	bp = xdr_encode_YFSStoreStatus_mode(bp, S_IRWXUGO);
+	bp = xdr_encode_YFSStoreStatus_mode(bp, is_mountpoint ? 0644 : S_IRWXUGO);
 	yfs_check_req(call, bp);
 
 	afs_use_fs_server(call, fc->cbi);


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ