fs-util: add XO_AUTO_RW_RO
This commit is contained in:
+91
-13
@@ -1191,6 +1191,9 @@ int xopenat_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_
|
||||
assert(!(FLAGS_SET(xopen_flags, XO_TRIGGER_AUTOMOUNT) && FLAGS_SET(xopen_flags, XO_SUBVOLUME)));
|
||||
assert(!(FLAGS_SET(xopen_flags, XO_TRIGGER_AUTOMOUNT) && FLAGS_SET(xopen_flags, XO_NOCOW)));
|
||||
|
||||
/* Don't specify an access mode if you want auto mode. */
|
||||
assert(!FLAGS_SET(xopen_flags, XO_AUTO_RW_RO) || (open_flags & O_ACCMODE_STRICT) == 0);
|
||||
|
||||
/* This is like openat(), but has a few tricks up its sleeves, extending behaviour:
|
||||
*
|
||||
* • O_DIRECTORY|O_CREAT is supported, which causes a directory to be created, and immediately
|
||||
@@ -1211,13 +1214,26 @@ int xopenat_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_
|
||||
* • If mode is specified as MODE_INVALID, we'll use 0755 for dirs, and 0644 for regular files.
|
||||
*
|
||||
* • The dir fd can be passed as XAT_FDROOT, in which case any relative paths will be taken relative to the root fs.
|
||||
*
|
||||
* • If XO_AUTO_RW_RO is specified and the file cannot be opened in O_RDWR mode due to EACCES/EROFS or similar, retry in O_RDONLY mode.
|
||||
*/
|
||||
|
||||
if (mode == MODE_INVALID)
|
||||
mode = (open_flags & O_DIRECTORY) ? 0755 : 0644;
|
||||
|
||||
if (FLAGS_SET(xopen_flags, XO_AUTO_RW_RO)) {
|
||||
if (open_flags & O_DIRECTORY) {
|
||||
/* Directories can only be opened in read-only mode */
|
||||
xopen_flags &= ~XO_AUTO_RW_RO;
|
||||
open_flags |= O_RDONLY;
|
||||
} else if (open_flags & O_PATH)
|
||||
/* O_PATH is incompatible with O_RDONLY/O_RDWR → fail */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (isempty(path)) {
|
||||
assert(!FLAGS_SET(open_flags, O_CREAT|O_EXCL));
|
||||
open_flags &= ~O_NOFOLLOW;
|
||||
|
||||
if (FLAGS_SET(xopen_flags, XO_REGULAR)) {
|
||||
r = fd_verify_regular(dir_fd);
|
||||
@@ -1231,7 +1247,16 @@ int xopenat_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_
|
||||
return r;
|
||||
}
|
||||
|
||||
return fd_reopen(dir_fd, open_flags & ~O_NOFOLLOW);
|
||||
if (FLAGS_SET(xopen_flags, XO_AUTO_RW_RO)) {
|
||||
/* First try: in r/w mode */
|
||||
fd = fd_reopen(dir_fd, open_flags|O_RDWR);
|
||||
if (!ERRNO_IS_NEG_FS_WRITE_REFUSED(fd) && fd != -EISDIR)
|
||||
return TAKE_FD(fd);
|
||||
|
||||
open_flags |= O_RDONLY;
|
||||
}
|
||||
|
||||
return fd_reopen(dir_fd, open_flags);
|
||||
}
|
||||
|
||||
_cleanup_close_ int _dir_fd = -EBADF;
|
||||
@@ -1292,10 +1317,23 @@ int xopenat_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_
|
||||
|
||||
} else if (FLAGS_SET(open_flags, O_CREAT|O_EXCL)) {
|
||||
/* In O_EXCL mode we can just create the thing, everything is dealt with for us */
|
||||
fd = openat(dir_fd, path, open_flags, mode);
|
||||
|
||||
if (FLAGS_SET(xopen_flags, XO_AUTO_RW_RO)) {
|
||||
fd = RET_NERRNO(openat(dir_fd, path, open_flags|O_RDWR, mode));
|
||||
if (ERRNO_IS_NEG_FS_WRITE_REFUSED(fd))
|
||||
open_flags |= O_RDONLY;
|
||||
else if (fd < 0) {
|
||||
r = fd;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (fd < 0) {
|
||||
r = -errno;
|
||||
goto error;
|
||||
fd = openat(dir_fd, path, open_flags, mode);
|
||||
if (fd < 0) {
|
||||
r = -errno;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
made_file = true;
|
||||
@@ -1309,10 +1347,24 @@ int xopenat_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_
|
||||
}
|
||||
|
||||
/* Doesn't exist yet, then try to create it */
|
||||
fd = openat(dir_fd, path, open_flags|O_CREAT|O_EXCL, mode);
|
||||
open_flags |= O_EXCL;
|
||||
|
||||
if (FLAGS_SET(xopen_flags, XO_AUTO_RW_RO)) {
|
||||
fd = RET_NERRNO(openat(dir_fd, path, open_flags|O_RDWR, mode));
|
||||
if (ERRNO_IS_NEG_FS_WRITE_REFUSED(fd))
|
||||
open_flags |= O_RDONLY;
|
||||
else if (fd < 0) {
|
||||
r = fd;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (fd < 0) {
|
||||
r = -errno;
|
||||
goto error;
|
||||
fd = openat(dir_fd, path, open_flags, mode);
|
||||
if (fd < 0) {
|
||||
r = -errno;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
made_file = true;
|
||||
@@ -1322,10 +1374,24 @@ int xopenat_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
fd = fd_reopen(inode_fd, open_flags & ~(O_NOFOLLOW|O_CREAT));
|
||||
open_flags &= ~(O_NOFOLLOW|O_CREAT);
|
||||
|
||||
if (FLAGS_SET(xopen_flags, XO_AUTO_RW_RO)) {
|
||||
fd = fd_reopen(inode_fd, open_flags|O_RDWR);
|
||||
if (ERRNO_IS_NEG_FS_WRITE_REFUSED(fd))
|
||||
open_flags |= O_RDONLY;
|
||||
else if (fd < 0) {
|
||||
r = fd;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (fd < 0) {
|
||||
r = fd;
|
||||
goto error;
|
||||
fd = fd_reopen(inode_fd, open_flags);
|
||||
if (fd < 0) {
|
||||
r = fd;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1338,10 +1404,22 @@ int xopenat_full(int dir_fd, const char *path, int open_flags, XOpenFlags xopen_
|
||||
} else {
|
||||
/* XO_SOCKET also lands here: it requires O_PATH (see asserts above) so openat() pins
|
||||
* the inode without connecting, and fd_verify_socket() below enforces the type. */
|
||||
fd = openat_report_new(dir_fd, path, open_flags, mode, &made_file);
|
||||
if (FLAGS_SET(xopen_flags, XO_AUTO_RW_RO)) {
|
||||
fd = openat_report_new(dir_fd, path, O_RDWR|open_flags, mode, &made_file);
|
||||
if (ERRNO_IS_NEG_FS_WRITE_REFUSED(fd) || fd == -EISDIR)
|
||||
open_flags |= O_RDONLY;
|
||||
else if (fd < 0) {
|
||||
r = fd;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (fd < 0) {
|
||||
r = fd;
|
||||
goto error;
|
||||
fd = openat_report_new(dir_fd, path, open_flags, mode, &made_file);
|
||||
if (fd < 0) {
|
||||
r = fd;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -115,6 +115,7 @@ typedef enum XOpenFlags {
|
||||
XO_REGULAR = 1 << 3, /* Fail if the inode is not a regular file */
|
||||
XO_SOCKET = 1 << 4, /* Fail if the inode is not a socket */
|
||||
XO_TRIGGER_AUTOMOUNT = 1 << 5, /* Trigger automounts via open_tree(). Requires O_PATH. */
|
||||
XO_AUTO_RW_RO = 1 << 6, /* Open in O_RDWR mode if possible, O_RDONLY if not */
|
||||
} XOpenFlags;
|
||||
|
||||
int open_mkdir_at_full(int dirfd, const char *path, int flags, XOpenFlags xopen_flags, mode_t mode);
|
||||
|
||||
Reference in New Issue
Block a user