From 551203c0f4affd69598f8e0217ff89e729d87781 Mon Sep 17 00:00:00 2001 From: cjihrig Date: Sat, 25 Jan 2020 16:44:36 -0500 Subject: [PATCH] add setup for new permissions APIs snapshot2 adds the following for working with file permissions: - A permissions data type representing read, write, execute, and "private" permissions. - path_permissions_set() which is similar to fchmodat(). - fd_permissions_set() which is similar to fchmod(). - Two new rights for calling the two new functions. - A permissions field added to the filestat type. - A permissions argument passed to path_open(). This will need to be verified against the wasi-libc once the changes land there. Based on the auto-generated WASI docs, it seems likely that existing constants will change/break. Refs: https://github.com/cjihrig/uvwasi/issues/59 --- README.md | 96 +++++++++++++++++++++++++++++ include/uvwasi.h | 10 +++ include/wasi_types.h | 9 +++ src/uv_mapping.c | 8 +++ src/uv_mapping.h | 2 + src/uvwasi.c | 84 ++++++++++++++++++++++++- src/wasi_rights.h | 5 ++ test/test-basic-file-io.c | 3 + test/test-ebadf-input-validation.c | 2 +- test/test-einval-input-validation.c | 11 +++- 10 files changed, 225 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index fbbbfc7..17c1ada 100644 --- a/README.md +++ b/README.md @@ -202,6 +202,7 @@ This section has been adapted from the official WASI API documentation. - [`uvwasi_fd_filestat_get()`](#fd_filestat_get) - [`uvwasi_fd_filestat_set_size()`](#fd_filestat_set_size) - [`uvwasi_fd_filestat_set_times()`](#fd_filestat_set_times) +- [`uvwasi_fd_permissions_set()`](#fd_permissions_set) - [`uvwasi_fd_pread()`](#fd_pread) - [`uvwasi_fd_prestat_get()`](#fd_prestat_get) - [`uvwasi_fd_prestat_dir_name()`](#fd_prestat_dir_name) @@ -218,6 +219,7 @@ This section has been adapted from the official WASI API documentation. - [`uvwasi_path_filestat_set_times()`](#path_filestat_set_times) - [`uvwasi_path_link()`](#path_link) - [`uvwasi_path_open()`](#path_open) +- [`uvwasi_path_permissions_set()`](#path_permissions_set) - [`uvwasi_path_readlink()`](#path_readlink) - [`uvwasi_path_remove_directory()`](#path_remove_directory) - [`uvwasi_path_rename()`](#path_rename) @@ -512,6 +514,27 @@ Inputs: A bitmask indicating which timestamps to adjust. +### `uvwasi_fd_permissions_set()` + +Set the permissions of a file or directory. + +This sets the permissions associated with a file or directory +in a filesystem at the time it is called. The ability to actually +access a file or directory may depend on additional permissions not +reflected here. + +Note: This is similar `fchmod` in POSIX. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) fd + + The file descriptor to operate on. + +- [\_\_wasi\_permissions\_t](#permissions) permissions + + The permissions associated with the file. + ### `uvwasi_fd_pread()` Read from a file descriptor, without using and updating the @@ -908,6 +931,10 @@ Inputs: The initial flags of the file descriptor. +- [\_\_wasi\_permissions\_t](#permissions) permissions + + If a file is created, the filesystem permissions to associate with it. + Outputs: - [\_\_wasi\_fd\_t](#fd) fd @@ -915,6 +942,39 @@ Outputs: The file descriptor of the file that has been opened. +### `uvwasi_path_permissions_set()` + +Set the permissions of a file or directory. + +This sets the permissions associated with a file or directory in a +filesystem at the time it is called. The ability to actually access +a file or directory may depend on additional permissions not reflected +here. + +Note: This is similar `fchmodat` in POSIX. + +Unlike POSIX, this doesn't expose a user/group/other distinction; +implementations in POSIX environments are suggested to consult the +umask to determine which of the user/group/other flags to modify. + +Inputs: + +- [\_\_wasi\_fd\_t](#fd) fd + + The file descriptor to operate on. + +- [\_\_wasi\_lookupflags\_t](#lookupflags) flags + + Flags determining the method of how the path is resolved. + +- const char \*path and size\_t path\_len + + The path to a file to query. + +- [\_\_wasi\_permissions\_t](#permissions) permissions + + The permissions associated with the file. + ### `uvwasi_path_readlink()` Read the contents of a symbolic link. @@ -1790,6 +1850,10 @@ Members: File type. +- [\_\_wasi\_permissions\_t](#permissions) permissions + + File permissions. + - [\_\_wasi\_linkcount\_t](#linkcount) st\_nlink Number of hard links to the file. @@ -1952,6 +2016,30 @@ Possible values: Truncate file to size 0. +### `uvwasi_permissions_t` (`uint8_t` bitfield) + +File permissions. This represents the permissions associated with a file in a filesystem, and don't fully reflect all the conditions which determine whether a given WASI program can access the file. + +Possible values: + +- **`UVWASI_PERMISSION_READ`** + + For files, permission to read the file. For directories, permission to do `readdir` and access files within the directory. + + Note: This is similar to the read bit being set on files, and the read *and* execute bits being set on directories, in POSIX. + +- **`UVWASI_PERMISSION_WRITE`** + + For files, permission to mutate the file. For directories, permission to create, remove, and rename items within the directory. + +- **`UVWASI_PERMISSION_EXECUTE`** + + For files, permission to "execute" the file, using whatever concept of "executing" the host filesystem has. This flag is not valid for directories. + +- **`UVWASI_PERMISSION_PRIVATE`** + + For filesystems which have a concept of multiple "users", this flag indicates that the file is only accessible by the effective "user" that the WASI store uses to access the filesystem, and inaccessible to other "users". + ### `uvwasi_riflags_t` (`uint16_t` bitfield) Flags provided to [`uvwasi_sock_recv()`](#sock_recv). @@ -2087,6 +2175,10 @@ Possible values: The right to invoke [`uvwasi_path_filestat_set_times()`](#path_filestat_set_times). +- **`UVWASI_RIGHT_PATH_PERMISSIONS_SET`** + + The right to invoke [`uvwasi_path_filestat_permissions_set()`](#path_filestat_permissions_set). + - **`UVWASI_RIGHT_FD_FILESTAT_GET`** The right to invoke [`uvwasi_fd_filestat_get()`](#fd_filestat_get). @@ -2099,6 +2191,10 @@ Possible values: The right to invoke [`uvwasi_fd_filestat_set_times()`](#fd_filestat_set_times). +- **`UVWASI_RIGHT_FD_PERMISSIONS_SET`** + + The right to invoke [`uvwasi_fd_filestat_permissions_set()`](#fd_filestat_permissions_set). + - **`UVWASI_RIGHT_PATH_SYMLINK`** The right to invoke [`uvwasi_path_symlink()`](#path_symlink). diff --git a/include/uvwasi.h b/include/uvwasi.h index e2b8484..5de5ec3 100644 --- a/include/uvwasi.h +++ b/include/uvwasi.h @@ -123,6 +123,9 @@ uvwasi_errno_t uvwasi_fd_filestat_set_times(uvwasi_t* uvwasi, uvwasi_timestamp_t st_atim, uvwasi_timestamp_t st_mtim, uvwasi_fstflags_t fst_flags); +uvwasi_errno_t uvwasi_fd_permissions_set(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_permissions_t permissions); uvwasi_errno_t uvwasi_fd_pread(uvwasi_t* uvwasi, uvwasi_fd_t fd, const uvwasi_iovec_t* iovs, @@ -205,7 +208,14 @@ uvwasi_errno_t uvwasi_path_open(uvwasi_t* uvwasi, uvwasi_rights_t fs_rights_base, uvwasi_rights_t fs_rights_inheriting, uvwasi_fdflags_t fs_flags, + uvwasi_permissions_t permissions, uvwasi_fd_t* fd); +uvwasi_errno_t uvwasi_path_permissions_set(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_lookupflags_t flags, + const char* path, + size_t path_len, + uvwasi_permissions_t permissions); uvwasi_errno_t uvwasi_path_readlink(uvwasi_t* uvwasi, uvwasi_fd_t fd, const char* path, diff --git a/include/wasi_types.h b/include/wasi_types.h index ab3cc69..6f83a6c 100644 --- a/include/wasi_types.h +++ b/include/wasi_types.h @@ -213,10 +213,18 @@ typedef uint64_t uvwasi_rights_t; /* Bitfield */ #define UVWASI_RIGHT_PATH_UNLINK_FILE (1 << 26) #define UVWASI_RIGHT_POLL_FD_READWRITE (1 << 27) #define UVWASI_RIGHT_SOCK_SHUTDOWN (1 << 28) +#define UVWASI_RIGHT_PATH_PERMISSIONS_SET (1 << 29) +#define UVWASI_RIGHT_FD_PERMISSIONS_SET (1 << 30) typedef uint16_t uvwasi_roflags_t; /* Bitfield */ #define UVWASI_SOCK_RECV_DATA_TRUNCATED (1 << 0) +typedef uint8_t uvwasi_permissions_t; /* Bitfield */ +#define UVWASI_PERMISSION_READ (1 << 0) +#define UVWASI_PERMISSION_WRITE (1 << 1) +#define UVWASI_PERMISSION_EXECUTE (1 << 2) +#define UVWASI_PERMISSION_PRIVATE (1 << 3) + typedef uint8_t uvwasi_sdflags_t; /* Bitfield */ #define UVWASI_SHUT_RD (1 << 0) #define UVWASI_SHUT_WR (1 << 1) @@ -264,6 +272,7 @@ typedef struct uvwasi_filestat_s { uvwasi_device_t st_dev; uvwasi_inode_t st_ino; uvwasi_filetype_t st_filetype; + uvwasi_permissions_t permissions; uvwasi_linkcount_t st_nlink; uvwasi_filesize_t st_size; uvwasi_timestamp_t st_atim; diff --git a/src/uv_mapping.c b/src/uv_mapping.c index 9314855..39d7bc1 100644 --- a/src/uv_mapping.c +++ b/src/uv_mapping.c @@ -155,6 +155,14 @@ void uvwasi__stat_to_filestat(const uv_stat_t* stat, uvwasi_filestat_t* fs) { fs->st_atim = uvwasi__timespec_to_timestamp(&stat->st_atim); fs->st_mtim = uvwasi__timespec_to_timestamp(&stat->st_mtim); fs->st_ctim = uvwasi__timespec_to_timestamp(&stat->st_ctim); + /* TODO(cjihrig): Permissions are not currently used. */ + fs->permissions = 0; +} + + +void uvwasi__permissions_to_mode(const uvwasi_permissions_t permissions, + int* mode) { + *mode = 0666; /* TODO(cjihrig): Parse this from permissions argument. */ } diff --git a/src/uv_mapping.h b/src/uv_mapping.h index 9d9bbd1..9d6f7a4 100644 --- a/src/uv_mapping.h +++ b/src/uv_mapping.h @@ -10,6 +10,8 @@ uvwasi_errno_t uvwasi__translate_uv_error(int err); uvwasi_timestamp_t uvwasi__timespec_to_timestamp(const uv_timespec_t* ts); uvwasi_filetype_t uvwasi__stat_to_filetype(const uv_stat_t* stat); void uvwasi__stat_to_filestat(const uv_stat_t* stat, uvwasi_filestat_t* fs); +void uvwasi__permissions_to_mode(const uvwasi_permissions_t permissions, + int* mode); uvwasi_errno_t uvwasi__get_filetype_by_fd(uv_file fd, uvwasi_filetype_t* type); #endif /* __UVWASI_UV_MAPPING_H__ */ diff --git a/src/uvwasi.c b/src/uvwasi.c index 2fb195f..1c3e54c 100644 --- a/src/uvwasi.c +++ b/src/uvwasi.c @@ -1165,6 +1165,38 @@ uvwasi_errno_t uvwasi_fd_filestat_set_times(uvwasi_t* uvwasi, } +uvwasi_errno_t uvwasi_fd_permissions_set(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_permissions_t permissions) { + struct uvwasi_fd_wrap_t* wrap; + uv_fs_t req; + uvwasi_errno_t err; + int mode; + int r; + + if (uvwasi == NULL) + return UVWASI_EINVAL; + + uvwasi__permissions_to_mode(permissions, &mode); + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_FD_PERMISSIONS_SET, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + r = uv_fs_fchmod(NULL, &req, wrap->fd, mode, NULL); + uv_mutex_unlock(&wrap->mutex); + uv_fs_req_cleanup(&req); + + if (r != 0) + return uvwasi__translate_uv_error(r); + + return UVWASI_ESUCCESS; +} + + uvwasi_errno_t uvwasi_fd_pread(uvwasi_t* uvwasi, uvwasi_fd_t fd, const uvwasi_iovec_t* iovs, @@ -1851,6 +1883,7 @@ uvwasi_errno_t uvwasi_path_open(uvwasi_t* uvwasi, uvwasi_rights_t fs_rights_base, uvwasi_rights_t fs_rights_inheriting, uvwasi_fdflags_t fs_flags, + uvwasi_permissions_t permissions, uvwasi_fd_t* fd) { char resolved_path[PATH_MAX_BYTES]; uvwasi_rights_t needed_inheriting; @@ -1865,11 +1898,13 @@ uvwasi_errno_t uvwasi_path_open(uvwasi_t* uvwasi, int flags; int read; int write; + int mode; int r; if (uvwasi == NULL || path == NULL || fd == NULL) return UVWASI_EINVAL; + uvwasi__permissions_to_mode(permissions, &mode); read = 0 != (fs_rights_base & (UVWASI_RIGHT_FD_READ | UVWASI_RIGHT_FD_READDIR)); write = 0 != (fs_rights_base & (UVWASI_RIGHT_FD_DATASYNC | @@ -1935,7 +1970,7 @@ uvwasi_errno_t uvwasi_path_open(uvwasi_t* uvwasi, return err; } - r = uv_fs_open(NULL, &req, resolved_path, flags, 0666, NULL); + r = uv_fs_open(NULL, &req, resolved_path, flags, mode, NULL); uv_mutex_unlock(&dirfd_wrap->mutex); uv_fs_req_cleanup(&req); @@ -1982,6 +2017,53 @@ uvwasi_errno_t uvwasi_path_open(uvwasi_t* uvwasi, } +uvwasi_errno_t uvwasi_path_permissions_set(uvwasi_t* uvwasi, + uvwasi_fd_t fd, + uvwasi_lookupflags_t flags, + const char* path, + size_t path_len, + uvwasi_permissions_t permissions) { + char resolved_path[PATH_MAX_BYTES]; + struct uvwasi_fd_wrap_t* wrap; + uv_fs_t req; + uvwasi_errno_t err; + int mode; + int r; + + if (uvwasi == NULL || path == NULL) + return UVWASI_EINVAL; + + uvwasi__permissions_to_mode(permissions, &mode); + err = uvwasi_fd_table_get(&uvwasi->fds, + fd, + &wrap, + UVWASI_RIGHT_PATH_PERMISSIONS_SET, + 0); + if (err != UVWASI_ESUCCESS) + return err; + + err = uvwasi__resolve_path(uvwasi, + wrap, + path, + path_len, + resolved_path, + flags); + if (err != UVWASI_ESUCCESS) { + uv_mutex_unlock(&wrap->mutex); + return err; + } + + r = uv_fs_chmod(NULL, &req, resolved_path, mode, NULL); + uv_mutex_unlock(&wrap->mutex); + uv_fs_req_cleanup(&req); + + if (r != 0) + return uvwasi__translate_uv_error(r); + + return UVWASI_ESUCCESS; +} + + uvwasi_errno_t uvwasi_path_readlink(uvwasi_t* uvwasi, uvwasi_fd_t fd, const char* path, diff --git a/src/wasi_rights.h b/src/wasi_rights.h index 6510bdf..bffeae4 100644 --- a/src/wasi_rights.h +++ b/src/wasi_rights.h @@ -24,9 +24,11 @@ UVWASI_RIGHT_PATH_FILESTAT_GET | \ UVWASI_RIGHT_PATH_FILESTAT_SET_SIZE | \ UVWASI_RIGHT_PATH_FILESTAT_SET_TIMES | \ + UVWASI_RIGHT_PATH_PERMISSIONS_SET | \ UVWASI_RIGHT_FD_FILESTAT_GET | \ UVWASI_RIGHT_FD_FILESTAT_SET_TIMES | \ UVWASI_RIGHT_FD_FILESTAT_SET_SIZE | \ + UVWASI_RIGHT_FD_PERMISSIONS_SET | \ UVWASI_RIGHT_PATH_SYMLINK | \ UVWASI_RIGHT_PATH_UNLINK_FILE | \ UVWASI_RIGHT_PATH_REMOVE_DIRECTORY | \ @@ -51,6 +53,7 @@ UVWASI_RIGHT_FD_FILESTAT_GET | \ UVWASI_RIGHT_FD_FILESTAT_SET_SIZE | \ UVWASI_RIGHT_FD_FILESTAT_SET_TIMES |\ + UVWASI_RIGHT_FD_PERMISSIONS_SET | \ UVWASI_RIGHT_POLL_FD_READWRITE) #define UVWASI__RIGHTS_REGULAR_FILE_INHERITING 0 @@ -69,8 +72,10 @@ UVWASI_RIGHT_PATH_FILESTAT_GET | \ UVWASI_RIGHT_PATH_FILESTAT_SET_SIZE | \ UVWASI_RIGHT_PATH_FILESTAT_SET_TIMES | \ + UVWASI_RIGHT_PATH_PERMISSIONS_SET | \ UVWASI_RIGHT_FD_FILESTAT_GET | \ UVWASI_RIGHT_FD_FILESTAT_SET_TIMES | \ + UVWASI_RIGHT_FD_PERMISSIONS_SET | \ UVWASI_RIGHT_PATH_SYMLINK | \ UVWASI_RIGHT_PATH_UNLINK_FILE | \ UVWASI_RIGHT_PATH_REMOVE_DIRECTORY | \ diff --git a/test/test-basic-file-io.c b/test/test-basic-file-io.c index d3ad8b8..dda773d 100644 --- a/test/test-basic-file-io.c +++ b/test/test-basic-file-io.c @@ -61,6 +61,7 @@ int main(void) { fs_rights_base, 0, 0, + 0, &fd); assert(err == 0); @@ -79,6 +80,7 @@ int main(void) { assert(stats.st_atim > 0); assert(stats.st_mtim > 0); assert(stats.st_ctim > 0); + assert(stats.permissions == 0); /* Resize the file to 1024 bytes. */ err = uvwasi_fd_filestat_set_size(&uvwasi, fd, 1024); @@ -163,6 +165,7 @@ int main(void) { assert(stats2.st_atim > 0); assert(stats2.st_mtim > 0); assert(stats2.st_ctim > 0); + assert(stats2.permissions == 0); /* Unlink the file. */ err = uvwasi_path_unlink_file(&uvwasi, 3, path, strlen(path) + 1); diff --git a/test/test-ebadf-input-validation.c b/test/test-ebadf-input-validation.c index 46f7d0a..72533bd 100644 --- a/test/test-ebadf-input-validation.c +++ b/test/test-ebadf-input-validation.c @@ -59,7 +59,7 @@ int main(void) { CHECK(uvwasi_path_filestat_get(&uvw, 100, 0, test_str, 5, &test_filestat)); CHECK(uvwasi_path_filestat_set_times(&uvw, 100, 0, test_str, 4, 5, 6, 7)); CHECK(uvwasi_path_link(&uvw, 100, 4, test_str, 5, 100, test_str, 6)); - CHECK(uvwasi_path_open(&uvw, 100, 0, test_str, 4, 5, 6, 7, 8, &test_fd)); + CHECK(uvwasi_path_open(&uvw, 100, 0, test_str, 4, 5, 6, 7, 8, 9, &test_fd)); CHECK(uvwasi_path_readlink(&uvw, 100, test_str, 4, test_str, 5, &test_size)); CHECK(uvwasi_path_remove_directory(&uvw, 99, "x", 2)); CHECK(uvwasi_path_rename(&uvw, 99, test_str, 4, 99, test_str, 5)); diff --git a/test/test-einval-input-validation.c b/test/test-einval-input-validation.c index f4eab44..bd24e6c 100644 --- a/test/test-einval-input-validation.c +++ b/test/test-einval-input-validation.c @@ -74,6 +74,8 @@ int main(void) { CHECK(uvwasi_fd_filestat_set_times(NULL, 3, 0, 0, UVWASI_FILESTAT_SET_ATIM)); + CHECK(uvwasi_fd_permissions_set(NULL, 3, 0)); + CHECK(uvwasi_fd_pread(NULL, 3, &test_iovec, 2, 10, &test_size)); CHECK(uvwasi_fd_pread(&uvw, 3, NULL, 2, 10, &test_size)); CHECK(uvwasi_fd_pread(&uvw, 3, &test_iovec, 2, 10, NULL)); @@ -124,9 +126,12 @@ int main(void) { CHECK(uvwasi_path_link(&uvw, 3, 4, NULL, 5, 3, test_str, 6)); CHECK(uvwasi_path_link(&uvw, 3, 4, test_str, 5, 3, NULL, 6)); - CHECK(uvwasi_path_open(NULL, 3, 0, test_str, 4, 5, 6, 7, 8, &test_fd)); - CHECK(uvwasi_path_open(&uvw, 3, 0, NULL, 4, 5, 6, 7, 8, &test_fd)); - CHECK(uvwasi_path_open(&uvw, 3, 0, test_str, 4, 5, 6, 7, 8, NULL)); + CHECK(uvwasi_path_open(NULL, 3, 0, test_str, 4, 5, 6, 7, 8, 9, &test_fd)); + CHECK(uvwasi_path_open(&uvw, 3, 0, NULL, 4, 5, 6, 7, 8, 9, &test_fd)); + CHECK(uvwasi_path_open(&uvw, 3, 0, test_str, 4, 5, 6, 7, 8, 9, NULL)); + + CHECK(uvwasi_path_permissions_set(NULL, 3, 1, test_str, 4, 5)); + CHECK(uvwasi_path_permissions_set(&uvw, 3, 1, NULL, 4, 5)); CHECK(uvwasi_path_readlink(NULL, 3, test_str, 4, test_str, 5, &test_size)); CHECK(uvwasi_path_readlink(&uvw, 3, NULL, 4, test_str, 5, &test_size));