Re: [Virtio-fs] [PATCH] virtiofsd: remove symlink fallbacks

2020-05-29 Thread Dr. David Alan Gilbert
* Vivek Goyal (vgo...@redhat.com) wrote:
> On Thu, May 14, 2020 at 04:07:36PM +0200, Miklos Szeredi wrote:
> > Path lookup in the kernel has special rules for looking up magic symlinks
> > under /proc.  If a filesystem operation is instructed to follow symlinks
> > (e.g. via AT_SYMLINK_FOLLOW or lack of AT_SYMLINK_NOFOLLOW), and the final
> > component is such a proc symlink, then the target of the magic symlink is
> > used for the operation, even if the target itself is a symlink.  I.e. path
> > lookup is always terminated after following a final magic symlink.
> > 
> > I was erronously assuming that in the above case the target symlink would
> > also be followed, and so workarounds were added for a couple of operations
> > to handle the symlink case.  Since the symlink can be handled simply by
> > following the proc symlink, these workardouds are not needed.
> > 
> > Also remove the "norace" option, which disabled the workarounds.
> > 
> > Commit bdfd66788349 ("virtiofsd: Fix xattr operations") already dealt with
> > the same issue for xattr operations.
> > 
> > Signed-off-by: Miklos Szeredi 
> 
> Good to have this cleanup.
> 
> Acked-by: Vivek Goyal 

Queued.

> Vivek
> 
> > ---
> >  tools/virtiofsd/passthrough_ll.c | 175 ++-
> >  1 file changed, 6 insertions(+), 169 deletions(-)
> > 
> > diff --git a/tools/virtiofsd/passthrough_ll.c 
> > b/tools/virtiofsd/passthrough_ll.c
> > index 3ba1d9098460..2ce7c96085bf 100644
> > --- a/tools/virtiofsd/passthrough_ll.c
> > +++ b/tools/virtiofsd/passthrough_ll.c
> > @@ -140,7 +140,6 @@ enum {
> >  struct lo_data {
> >  pthread_mutex_t mutex;
> >  int debug;
> > -int norace;
> >  int writeback;
> >  int flock;
> >  int posix_lock;
> > @@ -176,7 +175,6 @@ static const struct fuse_opt lo_opts[] = {
> >  { "cache=none", offsetof(struct lo_data, cache), CACHE_NONE },
> >  { "cache=auto", offsetof(struct lo_data, cache), CACHE_AUTO },
> >  { "cache=always", offsetof(struct lo_data, cache), CACHE_ALWAYS },
> > -{ "norace", offsetof(struct lo_data, norace), 1 },
> >  { "readdirplus", offsetof(struct lo_data, readdirplus_set), 1 },
> >  { "no_readdirplus", offsetof(struct lo_data, readdirplus_clear), 1 },
> >  FUSE_OPT_END
> > @@ -592,136 +590,6 @@ static void lo_getattr(fuse_req_t req, fuse_ino_t ino,
> >  fuse_reply_attr(req, , lo->timeout);
> >  }
> >  
> > -/*
> > - * Increments parent->nlookup and caller must release refcount using
> > - * lo_inode_put().
> > - */
> > -static int lo_parent_and_name(struct lo_data *lo, struct lo_inode *inode,
> > -  char path[PATH_MAX], struct lo_inode 
> > **parent)
> > -{
> > -char procname[64];
> > -char *last;
> > -struct stat stat;
> > -struct lo_inode *p;
> > -int retries = 2;
> > -int res;
> > -
> > -retry:
> > -sprintf(procname, "%i", inode->fd);
> > -
> > -res = readlinkat(lo->proc_self_fd, procname, path, PATH_MAX);
> > -if (res < 0) {
> > -fuse_log(FUSE_LOG_WARNING, "%s: readlink failed: %m\n", __func__);
> > -goto fail_noretry;
> > -}
> > -
> > -if (res >= PATH_MAX) {
> > -fuse_log(FUSE_LOG_WARNING, "%s: readlink overflowed\n", __func__);
> > -goto fail_noretry;
> > -}
> > -path[res] = '\0';
> > -
> > -last = strrchr(path, '/');
> > -if (last == NULL) {
> > -/* Shouldn't happen */
> > -fuse_log(
> > -FUSE_LOG_WARNING,
> > -"%s: INTERNAL ERROR: bad path read from proc\n", __func__);
> > -goto fail_noretry;
> > -}
> > -if (last == path) {
> > -p = >root;
> > -pthread_mutex_lock(>mutex);
> > -p->nlookup++;
> > -g_atomic_int_inc(>refcount);
> > -pthread_mutex_unlock(>mutex);
> > -} else {
> > -*last = '\0';
> > -res = fstatat(AT_FDCWD, last == path ? "/" : path, , 0);
> > -if (res == -1) {
> > -if (!retries) {
> > -fuse_log(FUSE_LOG_WARNING,
> > - "%s: failed to stat parent: %m\n", __func__);
> > -}
> > -goto fail;
> > -}
> > -p = lo_find(lo, );
> > -if (p == NULL) {
> > -if (!retries) {
> > -fuse_log(FUSE_LOG_WARNING,
> > - "%s: failed to find parent\n", __func__);
> > -}
> > -goto fail;
> > -}
> > -}
> > -last++;
> > -res = fstatat(p->fd, last, , AT_SYMLINK_NOFOLLOW);
> > -if (res == -1) {
> > -if (!retries) {
> > -fuse_log(FUSE_LOG_WARNING,
> > - "%s: failed to stat last\n", __func__);
> > -}
> > -goto fail_unref;
> > -}
> > -if (stat.st_dev != inode->key.dev || stat.st_ino != inode->key.ino) {
> > -if (!retries) {
> > -fuse_log(FUSE_LOG_WARNING,
> > - "%s: failed to match last\n", __func__);
> > -

Re: [Virtio-fs] [PATCH] virtiofsd: remove symlink fallbacks

2020-05-15 Thread Vivek Goyal
On Thu, May 14, 2020 at 04:07:36PM +0200, Miklos Szeredi wrote:
> Path lookup in the kernel has special rules for looking up magic symlinks
> under /proc.  If a filesystem operation is instructed to follow symlinks
> (e.g. via AT_SYMLINK_FOLLOW or lack of AT_SYMLINK_NOFOLLOW), and the final
> component is such a proc symlink, then the target of the magic symlink is
> used for the operation, even if the target itself is a symlink.  I.e. path
> lookup is always terminated after following a final magic symlink.
> 
> I was erronously assuming that in the above case the target symlink would
> also be followed, and so workarounds were added for a couple of operations
> to handle the symlink case.  Since the symlink can be handled simply by
> following the proc symlink, these workardouds are not needed.
> 
> Also remove the "norace" option, which disabled the workarounds.
> 
> Commit bdfd66788349 ("virtiofsd: Fix xattr operations") already dealt with
> the same issue for xattr operations.
> 
> Signed-off-by: Miklos Szeredi 

Good to have this cleanup.

Acked-by: Vivek Goyal 

Vivek

> ---
>  tools/virtiofsd/passthrough_ll.c | 175 ++-
>  1 file changed, 6 insertions(+), 169 deletions(-)
> 
> diff --git a/tools/virtiofsd/passthrough_ll.c 
> b/tools/virtiofsd/passthrough_ll.c
> index 3ba1d9098460..2ce7c96085bf 100644
> --- a/tools/virtiofsd/passthrough_ll.c
> +++ b/tools/virtiofsd/passthrough_ll.c
> @@ -140,7 +140,6 @@ enum {
>  struct lo_data {
>  pthread_mutex_t mutex;
>  int debug;
> -int norace;
>  int writeback;
>  int flock;
>  int posix_lock;
> @@ -176,7 +175,6 @@ static const struct fuse_opt lo_opts[] = {
>  { "cache=none", offsetof(struct lo_data, cache), CACHE_NONE },
>  { "cache=auto", offsetof(struct lo_data, cache), CACHE_AUTO },
>  { "cache=always", offsetof(struct lo_data, cache), CACHE_ALWAYS },
> -{ "norace", offsetof(struct lo_data, norace), 1 },
>  { "readdirplus", offsetof(struct lo_data, readdirplus_set), 1 },
>  { "no_readdirplus", offsetof(struct lo_data, readdirplus_clear), 1 },
>  FUSE_OPT_END
> @@ -592,136 +590,6 @@ static void lo_getattr(fuse_req_t req, fuse_ino_t ino,
>  fuse_reply_attr(req, , lo->timeout);
>  }
>  
> -/*
> - * Increments parent->nlookup and caller must release refcount using
> - * lo_inode_put().
> - */
> -static int lo_parent_and_name(struct lo_data *lo, struct lo_inode *inode,
> -  char path[PATH_MAX], struct lo_inode **parent)
> -{
> -char procname[64];
> -char *last;
> -struct stat stat;
> -struct lo_inode *p;
> -int retries = 2;
> -int res;
> -
> -retry:
> -sprintf(procname, "%i", inode->fd);
> -
> -res = readlinkat(lo->proc_self_fd, procname, path, PATH_MAX);
> -if (res < 0) {
> -fuse_log(FUSE_LOG_WARNING, "%s: readlink failed: %m\n", __func__);
> -goto fail_noretry;
> -}
> -
> -if (res >= PATH_MAX) {
> -fuse_log(FUSE_LOG_WARNING, "%s: readlink overflowed\n", __func__);
> -goto fail_noretry;
> -}
> -path[res] = '\0';
> -
> -last = strrchr(path, '/');
> -if (last == NULL) {
> -/* Shouldn't happen */
> -fuse_log(
> -FUSE_LOG_WARNING,
> -"%s: INTERNAL ERROR: bad path read from proc\n", __func__);
> -goto fail_noretry;
> -}
> -if (last == path) {
> -p = >root;
> -pthread_mutex_lock(>mutex);
> -p->nlookup++;
> -g_atomic_int_inc(>refcount);
> -pthread_mutex_unlock(>mutex);
> -} else {
> -*last = '\0';
> -res = fstatat(AT_FDCWD, last == path ? "/" : path, , 0);
> -if (res == -1) {
> -if (!retries) {
> -fuse_log(FUSE_LOG_WARNING,
> - "%s: failed to stat parent: %m\n", __func__);
> -}
> -goto fail;
> -}
> -p = lo_find(lo, );
> -if (p == NULL) {
> -if (!retries) {
> -fuse_log(FUSE_LOG_WARNING,
> - "%s: failed to find parent\n", __func__);
> -}
> -goto fail;
> -}
> -}
> -last++;
> -res = fstatat(p->fd, last, , AT_SYMLINK_NOFOLLOW);
> -if (res == -1) {
> -if (!retries) {
> -fuse_log(FUSE_LOG_WARNING,
> - "%s: failed to stat last\n", __func__);
> -}
> -goto fail_unref;
> -}
> -if (stat.st_dev != inode->key.dev || stat.st_ino != inode->key.ino) {
> -if (!retries) {
> -fuse_log(FUSE_LOG_WARNING,
> - "%s: failed to match last\n", __func__);
> -}
> -goto fail_unref;
> -}
> -*parent = p;
> -memmove(path, last, strlen(last) + 1);
> -
> -return 0;
> -
> -fail_unref:
> -unref_inode_lolocked(lo, p, 1);
> -lo_inode_put(lo, );
> -fail:
> -if (retries) {
> -retries--;
> -goto retry;
> -}
> -fail_noretry:
> 

Re: [Virtio-fs] [PATCH] virtiofsd: remove symlink fallbacks

2020-05-14 Thread Miklos Szeredi
On Fri, May 15, 2020 at 5:43 AM Liu Bo  wrote:
>
> On Thu, May 14, 2020 at 04:07:36PM +0200, Miklos Szeredi wrote:
> > Path lookup in the kernel has special rules for looking up magic symlinks
> > under /proc.  If a filesystem operation is instructed to follow symlinks
> > (e.g. via AT_SYMLINK_FOLLOW or lack of AT_SYMLINK_NOFOLLOW), and the final
> > component is such a proc symlink, then the target of the magic symlink is
> > used for the operation, even if the target itself is a symlink.  I.e. path
> > lookup is always terminated after following a final magic symlink.
> >
>
> Hi Miklos,
>
> Are these mentioned special rules supported by a recent kernel version
> or are they there from day one linux?

Hi Liubo,

AFAICS, these rules have been there from day one.

Thanks,
Miklos




Re: [Virtio-fs] [PATCH] virtiofsd: remove symlink fallbacks

2020-05-14 Thread Liu Bo
On Thu, May 14, 2020 at 04:07:36PM +0200, Miklos Szeredi wrote:
> Path lookup in the kernel has special rules for looking up magic symlinks
> under /proc.  If a filesystem operation is instructed to follow symlinks
> (e.g. via AT_SYMLINK_FOLLOW or lack of AT_SYMLINK_NOFOLLOW), and the final
> component is such a proc symlink, then the target of the magic symlink is
> used for the operation, even if the target itself is a symlink.  I.e. path
> lookup is always terminated after following a final magic symlink.
>

Hi Miklos,

Are these mentioned special rules supported by a recent kernel version
or are they there from day one linux?

thanks,
liubo

> I was erronously assuming that in the above case the target symlink would
> also be followed, and so workarounds were added for a couple of operations
> to handle the symlink case.  Since the symlink can be handled simply by
> following the proc symlink, these workardouds are not needed.
> 
> Also remove the "norace" option, which disabled the workarounds.
> 
> Commit bdfd66788349 ("virtiofsd: Fix xattr operations") already dealt with
> the same issue for xattr operations.
> 
> Signed-off-by: Miklos Szeredi 
> ---
>  tools/virtiofsd/passthrough_ll.c | 175 ++-
>  1 file changed, 6 insertions(+), 169 deletions(-)
> 
> diff --git a/tools/virtiofsd/passthrough_ll.c 
> b/tools/virtiofsd/passthrough_ll.c
> index 3ba1d9098460..2ce7c96085bf 100644
> --- a/tools/virtiofsd/passthrough_ll.c
> +++ b/tools/virtiofsd/passthrough_ll.c
> @@ -140,7 +140,6 @@ enum {
>  struct lo_data {
>  pthread_mutex_t mutex;
>  int debug;
> -int norace;
>  int writeback;
>  int flock;
>  int posix_lock;
> @@ -176,7 +175,6 @@ static const struct fuse_opt lo_opts[] = {
>  { "cache=none", offsetof(struct lo_data, cache), CACHE_NONE },
>  { "cache=auto", offsetof(struct lo_data, cache), CACHE_AUTO },
>  { "cache=always", offsetof(struct lo_data, cache), CACHE_ALWAYS },
> -{ "norace", offsetof(struct lo_data, norace), 1 },
>  { "readdirplus", offsetof(struct lo_data, readdirplus_set), 1 },
>  { "no_readdirplus", offsetof(struct lo_data, readdirplus_clear), 1 },
>  FUSE_OPT_END
> @@ -592,136 +590,6 @@ static void lo_getattr(fuse_req_t req, fuse_ino_t ino,
>  fuse_reply_attr(req, , lo->timeout);
>  }
>  
> -/*
> - * Increments parent->nlookup and caller must release refcount using
> - * lo_inode_put().
> - */
> -static int lo_parent_and_name(struct lo_data *lo, struct lo_inode *inode,
> -  char path[PATH_MAX], struct lo_inode **parent)
> -{
> -char procname[64];
> -char *last;
> -struct stat stat;
> -struct lo_inode *p;
> -int retries = 2;
> -int res;
> -
> -retry:
> -sprintf(procname, "%i", inode->fd);
> -
> -res = readlinkat(lo->proc_self_fd, procname, path, PATH_MAX);
> -if (res < 0) {
> -fuse_log(FUSE_LOG_WARNING, "%s: readlink failed: %m\n", __func__);
> -goto fail_noretry;
> -}
> -
> -if (res >= PATH_MAX) {
> -fuse_log(FUSE_LOG_WARNING, "%s: readlink overflowed\n", __func__);
> -goto fail_noretry;
> -}
> -path[res] = '\0';
> -
> -last = strrchr(path, '/');
> -if (last == NULL) {
> -/* Shouldn't happen */
> -fuse_log(
> -FUSE_LOG_WARNING,
> -"%s: INTERNAL ERROR: bad path read from proc\n", __func__);
> -goto fail_noretry;
> -}
> -if (last == path) {
> -p = >root;
> -pthread_mutex_lock(>mutex);
> -p->nlookup++;
> -g_atomic_int_inc(>refcount);
> -pthread_mutex_unlock(>mutex);
> -} else {
> -*last = '\0';
> -res = fstatat(AT_FDCWD, last == path ? "/" : path, , 0);
> -if (res == -1) {
> -if (!retries) {
> -fuse_log(FUSE_LOG_WARNING,
> - "%s: failed to stat parent: %m\n", __func__);
> -}
> -goto fail;
> -}
> -p = lo_find(lo, );
> -if (p == NULL) {
> -if (!retries) {
> -fuse_log(FUSE_LOG_WARNING,
> - "%s: failed to find parent\n", __func__);
> -}
> -goto fail;
> -}
> -}
> -last++;
> -res = fstatat(p->fd, last, , AT_SYMLINK_NOFOLLOW);
> -if (res == -1) {
> -if (!retries) {
> -fuse_log(FUSE_LOG_WARNING,
> - "%s: failed to stat last\n", __func__);
> -}
> -goto fail_unref;
> -}
> -if (stat.st_dev != inode->key.dev || stat.st_ino != inode->key.ino) {
> -if (!retries) {
> -fuse_log(FUSE_LOG_WARNING,
> - "%s: failed to match last\n", __func__);
> -}
> -goto fail_unref;
> -}
> -*parent = p;
> -memmove(path, last, strlen(last) + 1);
> -
> -return 0;
> -
> -fail_unref:
> -unref_inode_lolocked(lo, p, 1);
> -lo_inode_put(lo, );
> -fail:
> -if