linux-nfs.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Ephrim Khong <dr.khong@gmail.com>
To: linux-nfs <linux-nfs@vger.kernel.org>
Subject: [BUG?] Executable flag lost when O_EXCL is not set
Date: Tue, 9 Mar 2021 15:16:17 +0100	[thread overview]
Message-ID: <f84e3f21-e6eb-0698-0e6e-6f96dbed4ab7@gmail.com> (raw)

Hi,

I ran into an issue that might or might not be a bug in nfs4. When
creating a file that does not previously exist on my system with

  openat(AT_FDCWD, fname, O_CREAT | O_EXCL, 0777);

vs.

  openat(AT_FDCWD, fname, O_CREAT, 0777);

the file has permissions 0755 for the first version and 0600 for the
second. umask is 0022 in both cases, the calls are in the same program,
right after each other (with an unlink in between). I am mostly worried
about the executable bit for the owner, which is lost.

Executing the code on an ext4 filesystem "works", meaning that it
produces the same permissions for both openat calls, regardless of O_EXCL.

My questions would be
- Is that expected, or an indication that something is off?
- Could it be some issue in the backend, not in nfs4 itself?
- Can someone reproduce this on a NFS4 mount (test is below)?

Thanks
- Eph


System details
**************

The storage backend is some SPSC IBM System.

$> uname -a
Linux xxxx 5.4.0-66-generic #74-Ubuntu SMP Wed Jan 27 22:54:38 UTC 2021
x86_64 x86_64 x86_64 GNU/Linux

$> mount -vv |grep foo

10.0.11.183:/export/foo on /import/foo type nfs4
(rw,relatime,vers=4.0,rsize=1048576,wsize=1048576,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=x.x.x.x,local_lock=none,addr=x.x.x.x)

strace
******

This is an strace of a program that triggered this behaviour. Note how
the second openat call has no O_EXCL, but the last lstat reports 0640 as
mode:

 lstat("filename.sh", 0x7ffc2f148c20) = -1 ENOENT (No such file or
directory)
 openat(AT_FDCWD, "filename.sh", O_WRONLY|O_CREAT|O_EXCL, 0777) = 4
 write(4, "#!/bin/bash\n#\n# Build and run hb"..., 1973) = 1973
 fstat(4, {st_mode=S_IFREG|0755, st_size=1973, ...}) = 0
 close(4)                                = 0

 lstat("filename.sh", {st_mode=S_IFREG|0755, st_size=1973, ...}) = 0
 unlink("filename.sh") = 0
	
 openat(AT_FDCWD, "filename.sh", O_WRONLY|O_CREAT|O_TRUNC, 0777) = 4
 write(4, "#!/bin/bash\n#\n# Build and run hb"..., 1973) = 1973
 close(4)                                = 0
 lstat("filename.sh", {st_mode=S_IFREG|0640, st_size=1973, ...}) = 0

Test script to reproduce
************************
Reproduce with

  gcc test.c && ./a.out test_file

The reported st_modes should be identical.

-------------8<---------------

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <malloc.h>

#define FAIL() {fprintf(stderr, "Failed at %s:%d\n", \
    __FILE__,(int)__LINE__); exit(2); }

#define EXPECT(A,B) {if ((A) != (B)) FAIL(); }

int run_test(const char* const fname)
{
  int res;
  int fd;
  struct stat mystat;

  res = lstat(fname, &mystat);
  if (res != -1)
  {
    /* File exists, delete */
    res = unlink(fname);
    EXPECT(res, 0);
  }

  res = lstat(fname, &mystat);
  EXPECT(res, -1);

  /* Create file with O_EXCL */
  fd = openat(AT_FDCWD, fname, O_CREAT | O_EXCL, 0777);
  if (fd == -1)
    FAIL();

  res = close(fd);
  EXPECT(res, 0);

  res = lstat(fname, &mystat);
  EXPECT(res, 0);
  printf("st_mode after creating with O_EXCL: %4o\n", mystat.st_mode);

  /* Delete file */
  res = unlink(fname);
  EXPECT(res, 0);

  /* Create file without O_EXCL */
  fd = openat(AT_FDCWD, fname, O_CREAT, 0777);
  if (fd == -1)
    FAIL();

  res = close(fd);
  EXPECT(res, 0);

  res = lstat(fname, &mystat);
  EXPECT(res, 0);

  printf("st_mode after creating w/o O_EXCL:  %4o\n", mystat.st_mode);
}

int main(int argc, const char** argv)
{
  if (argc < 2)
  {
    printf("Delete and re-create a file with different modes,\n");
    printf("checking the file permissions bits each time.\n");
    printf("Usage:\n");
    printf("    %s  <filename>\n", argv[0]);
    printf("ATTENTION: The passed filename will be deleted.\n");
    return 1;
  }

  const char* fname = argv[1];
  return run_test(fname);
}

                 reply	other threads:[~2021-03-09 14:17 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

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=f84e3f21-e6eb-0698-0e6e-6f96dbed4ab7@gmail.com \
    --to=dr.khong@gmail.com \
    --cc=linux-nfs@vger.kernel.org \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).