linux-nfs.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [BUG?] Executable flag lost when O_EXCL is not set
@ 2021-03-09 14:16 Ephrim Khong
  0 siblings, 0 replies; only message in thread
From: Ephrim Khong @ 2021-03-09 14:16 UTC (permalink / raw)
  To: linux-nfs

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);
}

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2021-03-09 14:17 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2021-03-09 14:16 [BUG?] Executable flag lost when O_EXCL is not set Ephrim Khong

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).