diff mbox series

[1/2] tools/helpers: Introduce cmp-fd-file-inode utility

Message ID 20200226152013.12200-2-jandryuk@gmail.com (mailing list archive)
State New, archived
Headers show
Series Remove locking.sh dependency on perl | expand

Commit Message

Jason Andryuk Feb. 26, 2020, 3:20 p.m. UTC
This is a C implementation of the perl code inside of locking.sh to
check that the locked file descriptor and lock file share the same inode
and therefore match.  One change from the perl version is replacing
printing "y" on success with exit values of 0 (shell True) and 1 (shell
False).

Requiring perl is a large dependency for the single use, so a dedicated
utility removes that dependency for systems that otherwise would not
install perl.

Signed-off-by: Jason Andryuk <jandryuk@gmail.com>
---
 .gitignore                        |  1 +
 tools/helpers/Makefile            |  3 +++
 tools/helpers/cmp-fd-file-inode.c | 43 +++++++++++++++++++++++++++++++
 3 files changed, 47 insertions(+)
 create mode 100644 tools/helpers/cmp-fd-file-inode.c

Comments

Ian Jackson Feb. 26, 2020, 3:48 p.m. UTC | #1
Jason Andryuk writes ("[PATCH 1/2] tools/helpers: Introduce cmp-fd-file-inode utility"):
> This is a C implementation of the perl code inside of locking.sh to
> check that the locked file descriptor and lock file share the same inode
> and therefore match.  One change from the perl version is replacing
> printing "y" on success with exit values of 0 (shell True) and 1 (shell
> False).

Maybe it would be better to use stat(1) ?  On Linux
   stat -L -c%D.%i /dev/stdin blah.lock
or some such, and then compare the two numbers.

I'm reluctant to host a general-purpose shell utility in xen.git, no
matter how useful...

Thanks,
Ian.
Jason Andryuk Feb. 26, 2020, 4:05 p.m. UTC | #2
On Wed, Feb 26, 2020 at 10:48 AM Ian Jackson <ian.jackson@citrix.com> wrote:
>
> Jason Andryuk writes ("[PATCH 1/2] tools/helpers: Introduce cmp-fd-file-inode utility"):
> > This is a C implementation of the perl code inside of locking.sh to
> > check that the locked file descriptor and lock file share the same inode
> > and therefore match.  One change from the perl version is replacing
> > printing "y" on success with exit values of 0 (shell True) and 1 (shell
> > False).
>
> Maybe it would be better to use stat(1) ?  On Linux
>    stat -L -c%D.%i /dev/stdin blah.lock
> or some such, and then compare the two numbers.
>
> I'm reluctant to host a general-purpose shell utility in xen.git, no
> matter how useful...

Thanks for taking a look.

I'd be happy to use stat if it works.  The comment in locking.sh above
the usage is:
        # We can't just stat /dev/stdin or /proc/self/fd/$_lockfd or
        # use bash's test -ef because those all go through what is
        # actually a synthetic symlink in /proc and we aren't
        # guaranteed that our stat(2) won't lose the race with an
        # rm(1) between reading the synthetic link and traversing the
        # file system to find the inum.  Perl is very fast so use that.

...which I thought ruled out stat.

Regards,
Jason
George Dunlap March 5, 2020, 5:56 p.m. UTC | #3
On Wed, Feb 26, 2020 at 3:49 PM Ian Jackson <ian.jackson@citrix.com> wrote:

> Jason Andryuk writes ("[PATCH 1/2] tools/helpers: Introduce
> cmp-fd-file-inode utility"):
> > This is a C implementation of the perl code inside of locking.sh to
> > check that the locked file descriptor and lock file share the same inode
> > and therefore match.  One change from the perl version is replacing
> > printing "y" on success with exit values of 0 (shell True) and 1 (shell
> > False).
>
> Maybe it would be better to use stat(1) ?  On Linux
>    stat -L -c%D.%i /dev/stdin blah.lock
> or some such, and then compare the two numbers.
>
> I'm reluctant to host a general-purpose shell utility in xen.git, no
> matter how useful...
>

Do you have any other suggestions?

I agree it's not great to have loads of little helper programs lying
around.  But it's a lot better than pulling in a full perl installation for
a single line.

I sort of feel like part of the issue is that this is written in shell at
all.  The necessity to fall back to perl seems to me to indicate that bash
is the wrong language for what needs to happen here.  If locking.sh were
locking.c instead, this entire series probably wouldn't be necessary.

If no better options are forthcoming, I think we should accept something
like this until something better comes along.

 -George
Ian Jackson March 5, 2020, 7:12 p.m. UTC | #4
Jason Andryuk writes ("Re: [PATCH 1/2] tools/helpers: Introduce cmp-fd-file-inode utility"):
> I'd be happy to use stat if it works.  The comment in locking.sh above
> the usage is:
>         # We can't just stat /dev/stdin or /proc/self/fd/$_lockfd or
>         # use bash's test -ef because those all go through what is
>         # actually a synthetic symlink in /proc and we aren't
>         # guaranteed that our stat(2) won't lose the race with an
>         # rm(1) between reading the synthetic link and traversing the
>         # file system to find the inum.  Perl is very fast so use that.
> 
> ...which I thought ruled out stat.

Well read.

I have done some more testing and in my tests (on Debian stretch)
    stat -L - <some-file
does this
    fstat64(0, {st_mode=S_IFREG|0664, st_size=117844, ...}) = 0
(according to strace) which is precisely what is needed.

Oddly, it also does this
    fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 171), ...}) = 0
but it doesn't seem to do anything with the results, so I think
that's harmless.

I wrote that comment in 2012.  Presumably `stat -L -' has appeared in
the meantime.

The synthetic symlink may be a red herring anyway; nowadays at least,
I am told by someone who read the Linux kernel source that
  the name comes from the `readlink' method on the link inode, but a
  different method entirely -- `get_link' -- is used by `namei' to
  actually resolve the link to a destination inode.

But using `-' is clearly fine, like this I think:

mariner:~> stat -c%D.%i -L - t <t
fe04.844307
fe04.844307
mariner:~>

Sorry to muddy the waters.

Ian.
Jason Andryuk March 6, 2020, 1:47 p.m. UTC | #5
On Thu, Mar 5, 2020 at 2:12 PM Ian Jackson <ian.jackson@citrix.com> wrote:
>
> Jason Andryuk writes ("Re: [PATCH 1/2] tools/helpers: Introduce cmp-fd-file-inode utility"):
> > I'd be happy to use stat if it works.  The comment in locking.sh above
> > the usage is:
> >         # We can't just stat /dev/stdin or /proc/self/fd/$_lockfd or
> >         # use bash's test -ef because those all go through what is
> >         # actually a synthetic symlink in /proc and we aren't
> >         # guaranteed that our stat(2) won't lose the race with an
> >         # rm(1) between reading the synthetic link and traversing the
> >         # file system to find the inum.  Perl is very fast so use that.
> >
> > ...which I thought ruled out stat.
>
> Well read.
>
> I have done some more testing and in my tests (on Debian stretch)
>     stat -L - <some-file
> does this
>     fstat64(0, {st_mode=S_IFREG|0664, st_size=117844, ...}) = 0
> (according to strace) which is precisely what is needed.
>
> Oddly, it also does this
>     fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 171), ...}) = 0
> but it doesn't seem to do anything with the results, so I think
> that's harmless.

I think this stat is from glibc before printing.  `stat file -`
re-orders the fstat(1) before fstat(0).

> I wrote that comment in 2012.  Presumably `stat -L -' has appeared in
> the meantime.

I peaked at coreutils git, and - was added in 2009.  But I had no idea
of the magic '-'.  Thank you for finding it.

> The synthetic symlink may be a red herring anyway; nowadays at least,
> I am told by someone who read the Linux kernel source that
>   the name comes from the `readlink' method on the link inode, but a
>   different method entirely -- `get_link' -- is used by `namei' to
>   actually resolve the link to a destination inode.
>
> But using `-' is clearly fine, like this I think:
>
> mariner:~> stat -c%D.%i -L - t <t
> fe04.844307
> fe04.844307
> mariner:~>
>
> Sorry to muddy the waters.

Thanks again for finding the solution.

Regards,
Jason
diff mbox series

Patch

diff --git a/.gitignore b/.gitignore
index 4ca679ddbc..897f878eef 100644
--- a/.gitignore
+++ b/.gitignore
@@ -164,6 +164,7 @@  tools/fuzz/x86_instruction_emulator/x86-emulate.[ch]
 tools/helpers/_paths.h
 tools/helpers/init-xenstore-domain
 tools/helpers/xen-init-dom0
+tools/helpers/cmp-fd-file-inode
 tools/hotplug/common/hotplugpath.sh
 tools/hotplug/FreeBSD/rc.d/xencommons
 tools/hotplug/FreeBSD/rc.d/xendriverdomain
diff --git a/tools/helpers/Makefile b/tools/helpers/Makefile
index f759528322..7daf5c46ca 100644
--- a/tools/helpers/Makefile
+++ b/tools/helpers/Makefile
@@ -8,6 +8,7 @@  include $(XEN_ROOT)/tools/Rules.mk
 PROGS += xen-init-dom0
 ifeq ($(CONFIG_Linux),y)
 PROGS += init-xenstore-domain
+PROGS += cmp-fd-file-inode
 endif
 
 XEN_INIT_DOM0_OBJS = xen-init-dom0.o init-dom-json.o
@@ -40,12 +41,14 @@  install: all
 	$(INSTALL_PROG) xen-init-dom0 $(DESTDIR)$(LIBEXEC_BIN)
 ifeq ($(CONFIG_Linux),y)
 	$(INSTALL_PROG) init-xenstore-domain $(DESTDIR)$(LIBEXEC_BIN)
+	$(INSTALL_PROG) cmp-fd-file-inode $(DESTDIR)$(LIBEXEC_BIN)
 endif
 
 .PHONY: uninstall
 uninstall:
 ifeq ($(CONFIG_Linux),y)
 	rm -f $(DESTDIR)$(LIBEXEC_BIN)/init-xenstore-domain
+	rm -f $(DESTDIR)$(LIBEXEC_BIN)/cmp-fd-file-inode
 endif
 	rm -f $(DESTDIR)$(LIBEXEC_BIN)/xen-init-dom0
 
diff --git a/tools/helpers/cmp-fd-file-inode.c b/tools/helpers/cmp-fd-file-inode.c
new file mode 100644
index 0000000000..886ea888ed
--- /dev/null
+++ b/tools/helpers/cmp-fd-file-inode.c
@@ -0,0 +1,43 @@ 
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+void usage(const char * prog)
+{
+    fprintf(stderr,
+"%s <fd> <filename>\n"
+"Checks that the open file descriptor (referenced by number) has the same\n"
+"inode as the filename.\n"
+"Returns 0 on match and 1 on non-match\n", prog);
+}
+
+int main(int argc, char *argv[])
+{
+    struct stat fd_statbuf, file_statbuf;
+    int ret;
+    int fd;
+
+    if (argc < 3) {
+        usage(argv[0]);
+        return 1;
+    }
+
+    fd = strtoul(argv[1], NULL, 0);
+
+    ret = fstat(fd, &fd_statbuf);
+    if (ret) {
+        return 1;
+    }
+
+    ret = stat(argv[2], &file_statbuf);
+    if (ret) {
+        return 1;
+    }
+
+    if (fd_statbuf.st_ino == file_statbuf.st_ino)
+        return 0;
+    else
+        return 1;
+}