Project

General

Profile

Bug #58962

ftruncate fails with EACCES on a read-only file created with write permissions

Added by Bruno Passeri 12 months ago. Updated 11 months ago.

Status:
New
Priority:
Normal
Assignee:
Category:
Correctness/Safety
Target version:
% Done:

0%

Source:
Tags:
Backport:
reef,quincy,pacific
Regression:
No
Severity:
3 - minor
Reviewed:
Affected Versions:
ceph-qa-suite:
Component(FS):
ceph-fuse
Labels (FS):
Pull request ID:
Crash signature (v1):
Crash signature (v2):

Description

When creating a new file with write permissions, with mode set to read-only such as 400 or 444, ftruncate fails with EACCES. This has been a problem recently discovered, that has been around since at least August 2020. I have confirmed this with ceph-fuse, but I'm unsure if the same problem exists within the Linux Ceph kernel driver.

The snippet I used to test this behaviour:

#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

void print(char *verb, int perms) {
  fprintf(stdout, "%s with permissions %o: error #%d %s\n", verb, perms, errno, strerror(errno));
}

int main(int argc, char *argv[]) {
  const char *fn = "./ceph/testfile";
  const int permissions[2] = { 0666, 0444 };
  int fd;
  int i;
  for (i = 0; i < 2; i++) {
    if ((fd = open(fn, O_CREAT | O_WRONLY, permissions[i])) < 0) {
      print("Can't create", permissions[i]);
    } else {
      if (ftruncate(fd, 0) != 0) {
        print("Can't truncate", permissions[i]);
      } else {
        print("Succesfully truncated", permissions[i]);
      }
      close(fd);
      unlink(fn);
    }
  }
}

The expected output since both files are created with write permissions would be to complete successfully, but the second operation results in failure instead:

$ ./ceph-test
Succesfully truncated with permissions 666: error #0 Success
Can't truncate with permissions 444: error #13 Permission denied
Additionally, the EACCES return value is invalid as per the standards, because either we have a valid writable file descriptor, or it should return EBADF/EINVAL:

I'll also leave a link to the relevant discussion with Node/libuv where we encountered this problem, which has additional information albeit tangential: https://github.com/libuv/libuv/issues/3919

History

#1 Updated by Bruno Passeri 12 months ago

Forgot to mention that this has been recently verified on v15.2.17 and v16.2.11.

#2 Updated by Venky Shankar 12 months ago

Apologies for looking into this rather late, Bruno.

#3 Updated by Venky Shankar 11 months ago

  • Assignee set to Venky Shankar
  • Target version set to v18.0.0
  • Backport set to pacific,quincy

#4 Updated by Venky Shankar 11 months ago

This happens when setattr is called on truncate and looks like an incorrect check in inode_permission().

2023-04-05T06:18:22.819-0400 7fd4fa7fc6c0  8 client.474148 _ll_setattrx 0x10000000008.head mask 20
2023-04-05T06:18:22.819-0400 7fd4fa7fc6c0 20 client.474148 may_setattr 0x10000000008.head(faked_ino=0 nref=10 ll_ref=2 cap_refs={} open={2=1} mode=100444 size=0/4194304 nlink=1 btime=2023-04-05T06:18:22.820259-0400 mtime=2023-04-05T06:18:22.820259-0400 ctime=2023-04-05T06:18:22.820259-0400 change_attr=0 caps=pAsxLsXsxFsxcrwb(0=pAsxLsXsxFsxcrwb) objectset[0x10000000008 ts 0/0 objects 0 dirty_or_tx 0] parents=0x1.head["testfile"] 0x7fd504008330); UserPerm(uid: 1000, gid: 1000)
2023-04-05T06:18:22.819-0400 7fd4fa7fc6c0 10 client.474148 _getattr mask As issued=1
2023-04-05T06:18:22.819-0400 7fd4fa7fc6c0  3 client.474148 may_setattr 0x7fd504008330 = -13
2023-04-05T06:18:22.819-0400 7fd4fa7fc6c0  3 client.474148 ll_setattr 0x10000000008.head = -13

I'll push a fix.

#5 Updated by Venky Shankar 11 months ago

  • Category set to Correctness/Safety
  • Target version changed from v18.0.0 to v19.0.0
  • Backport changed from pacific,quincy to reef,quincy,pacific

#6 Updated by Venky Shankar 11 months ago

ftruncate end up calling Client::ll_setattr() with size=0 in ceph-fuse. Note that there is no file handle (fh) that's passed to userspace. Now, this piece of code is what causes the failure (from Client::ll_setattrx):

  if (!fuse_default_permissions) {
    int res = may_setattr(in, stx, mask, perms);
    if (res < 0)
      return res;
  }

This check is performed when fuse_default_permissions is unset (default). Setting this config to true (`fuse default permissions = true`) fixes the issue. You could use that if use of Posix ACLs are not desired.

#7 Updated by Venky Shankar 11 months ago

And BTW, the linux kernel driver does not have this issue.

Also available in: Atom PDF