diff --git a/sp.h b/sp.h index c4f94e2..ee194f7 100644 --- a/sp.h +++ b/sp.h @@ -1362,6 +1362,18 @@ typedef enum { SP_FS_KIND_SYMLINK, } sp_fs_kind_t; +typedef struct { + u32 value; +} sp_sys_file_perms_t; + +#if defined(SP_WIN32) + #define sp_sys_default_file_perms (SP_RVAL(sp_sys_file_perms_t) { .value = 0 }) + #define sp_sys_default_dir_perms (SP_RVAL(sp_sys_file_perms_t) { .value = 0 }) +#else + #define sp_sys_default_file_perms (SP_RVAL(sp_sys_file_perms_t) { .value = 0666 }) + #define sp_sys_default_dir_perms (SP_RVAL(sp_sys_file_perms_t) { .value = 0777 }) +#endif + typedef struct { sp_fs_kind_t kind; s64 size; @@ -1371,7 +1383,7 @@ typedef struct { u64 id; u64 device; u64 nlink; - u32 raw_attrs; + sp_sys_file_perms_t perms; } sp_sys_file_meta_t; typedef struct { @@ -1446,7 +1458,7 @@ SP_API sp_err_t sp_sys_open_dir(sp_sys_fd_t fd, const c8* path, u32 len, sp_s SP_API sp_err_t sp_sys_close(sp_sys_fd_t fd); SP_API sp_err_t sp_sys_pipe(sp_sys_pipe_t* pipe, sp_sys_pipe_desc_t desc); SP_API sp_err_t sp_sys_pipe_ready(sp_sys_fd_t fd, u8* ready); -SP_API sp_err_t sp_sys_mkdir(sp_sys_fd_t fd, const c8* path, u32 len, s32 mode); +SP_API sp_err_t sp_sys_mkdir(sp_sys_fd_t fd, const c8* path, u32 len, sp_sys_file_perms_t perms); SP_API sp_err_t sp_sys_rmdir(sp_sys_fd_t fd, const c8* path, u32 len); SP_API sp_err_t sp_sys_unlink(sp_sys_fd_t fd, const c8* path, u32 len); SP_API sp_err_t sp_sys_rename(sp_sys_fd_t from, const c8* pfrom, u32 lf, sp_sys_fd_t to, const c8* pto, u32 lt); @@ -1456,7 +1468,7 @@ SP_API sp_err_t sp_sys_readlink(sp_sys_fd_t fd, const c8* path, u32 len, c8* SP_API sp_err_t sp_sys_get_path_metadata(sp_sys_fd_t fd, const c8* path, u32 len, sp_sys_file_meta_t* st); SP_API sp_err_t sp_sys_get_link_metadata(sp_sys_fd_t fd, const c8* path, u32 len, sp_sys_file_meta_t* st); SP_API sp_err_t sp_sys_get_file_metadata(sp_sys_fd_t fd, sp_sys_file_meta_t* st); -SP_API sp_err_t sp_sys_chmod(sp_sys_fd_t fd, const c8* path, u32 len, const sp_sys_file_meta_t* st); +SP_API sp_err_t sp_sys_set_file_perms(sp_sys_fd_t fd, const c8* path, u32 len, sp_sys_file_perms_t perms); SP_API sp_err_t sp_sys_set_times(sp_sys_fd_t fd, const c8* path, u32 len, sp_sys_timespec_t atime, sp_sys_timespec_t mtime); SP_API sp_err_t sp_sys_clock_gettime(s32 clockid, sp_sys_timespec_t* ts); SP_API sp_err_t sp_sys_nanosleep(const sp_sys_timespec_t* req, sp_sys_timespec_t* rem); @@ -1506,7 +1518,7 @@ SP_API sp_err_t sp_sys_open_s(sp_sys_fd_t fd, sp_str_t path, sp_sys_open_mode SP_API sp_err_t sp_sys_open_dir_s(sp_sys_fd_t fd, sp_str_t path, sp_sys_fd_t* out); SP_API sp_err_t sp_sys_get_path_metadata_s(sp_sys_fd_t fd, sp_str_t path, sp_sys_file_meta_t* st); SP_API sp_err_t sp_sys_get_link_metadata_s(sp_sys_fd_t fd, sp_str_t path, sp_sys_file_meta_t* st); -SP_API sp_err_t sp_sys_mkdir_s(sp_sys_fd_t fd, sp_str_t path, s32 mode); +SP_API sp_err_t sp_sys_mkdir_s(sp_sys_fd_t fd, sp_str_t path, sp_sys_file_perms_t perms); SP_API sp_err_t sp_sys_rmdir_s(sp_sys_fd_t fd, sp_str_t path); SP_API sp_err_t sp_sys_unlink_s(sp_sys_fd_t fd, sp_str_t path); SP_API sp_err_t sp_sys_rename_s(sp_sys_fd_t from_fd, sp_str_t from, sp_sys_fd_t to_fd, sp_str_t to); @@ -1514,13 +1526,16 @@ SP_API sp_err_t sp_sys_chdir_s(sp_str_t path); SP_API sp_err_t sp_sys_link_s(sp_sys_fd_t from_fd, sp_str_t existing, sp_sys_fd_t to_fd, sp_str_t alias); SP_API sp_err_t sp_sys_symlink_s(sp_str_t existing, sp_sys_fd_t to_fd, sp_str_t alias); SP_API sp_err_t sp_sys_readlink_s(sp_sys_fd_t fd, sp_str_t path, c8* buf, u64 size, sp_str_t* target); -SP_API sp_err_t sp_sys_chmod_s(sp_sys_fd_t fd, sp_str_t path, const sp_sys_file_meta_t* st); +SP_API sp_err_t sp_sys_set_file_perms_s(sp_sys_fd_t fd, sp_str_t path, sp_sys_file_perms_t perms); SP_API sp_err_t sp_sys_set_times_s(sp_sys_fd_t fd, sp_str_t path, sp_sys_timespec_t atime, sp_sys_timespec_t mtime); SP_API s64 sp_sys_canonicalize_path_s(sp_str_t path, c8* buf, u64 size); SP_API sp_err_t sp_sys_dir_from_fd(sp_sys_fd_t fd, sp_sys_dir_t* out); SP_API sp_err_t sp_sys_dir_read(sp_sys_dir_t* dir, sp_mem_buffer_t* buf); SP_API sp_err_t sp_sys_dir_parse(sp_sys_dir_t* dir, sp_mem_buffer_t* buf, u64* cursor, sp_sys_dir_entry_t* out); SP_API sp_err_t sp_sys_dir_close(sp_sys_dir_t* dir); +SP_API bool sp_sys_is_read_only(sp_sys_file_perms_t perms); +SP_API void sp_sys_set_read_only(sp_sys_file_perms_t* perms, bool read_only); +SP_API void sp_sys_set_executable(sp_sys_file_perms_t* perms, bool executable); SP_API sp_err_t sp_tty_set_mode(sp_sys_fd_t in, sp_sys_fd_t out, sp_sys_tty_mode_t mode, sp_sys_tty_state_t* saved); SP_API sp_err_t sp_tty_restore(sp_sys_fd_t in, sp_sys_fd_t out, const sp_sys_tty_state_t* saved); @@ -1543,7 +1558,7 @@ typedef struct { sp_err_t (*close)(sp_sys_fd_t fd); sp_err_t (*pipe)(sp_sys_pipe_t* pipe, sp_sys_pipe_desc_t desc); sp_err_t (*pipe_ready)(sp_sys_fd_t fd, u8* ready); - sp_err_t (*mkdir)(sp_sys_fd_t fd, const c8* path, u32 len, s32 mode); + sp_err_t (*mkdir)(sp_sys_fd_t fd, const c8* path, u32 len, sp_sys_file_perms_t perms); sp_err_t (*rmdir)(sp_sys_fd_t fd, const c8* path, u32 len); sp_err_t (*unlink)(sp_sys_fd_t fd, const c8* path, u32 len); sp_err_t (*rename)(sp_sys_fd_t from_fd, const c8* from, u32 from_len, sp_sys_fd_t to_fd, const c8* to, u32 to_len); @@ -1553,7 +1568,7 @@ typedef struct { sp_err_t (*get_path_metadata)(sp_sys_fd_t fd, const c8* path, u32 len, sp_sys_file_meta_t* st); sp_err_t (*get_link_metadata)(sp_sys_fd_t fd, const c8* path, u32 len, sp_sys_file_meta_t* st); sp_err_t (*get_file_metadata)(sp_sys_fd_t fd, sp_sys_file_meta_t* st); - sp_err_t (*chmod)(sp_sys_fd_t fd, const c8* path, u32 len, const sp_sys_file_meta_t* st); + sp_err_t (*set_file_perms)(sp_sys_fd_t fd, const c8* path, u32 len, sp_sys_file_perms_t perms); sp_err_t (*set_times)(sp_sys_fd_t fd, const c8* path, u32 len, sp_sys_timespec_t atime, sp_sys_timespec_t mtime); sp_err_t (*clock_gettime)(s32 clockid, sp_sys_timespec_t* ts); sp_err_t (*nanosleep)(const sp_sys_timespec_t* req, sp_sys_timespec_t* rem); @@ -1618,7 +1633,7 @@ SP_API sp_err_t sp_sys_open_dir_p(sp_sys_fd_t fd, const c8* path, u32 len, sp SP_API sp_err_t sp_sys_close_p(sp_sys_fd_t fd); SP_API sp_err_t sp_sys_pipe_p(sp_sys_pipe_t* pipe, sp_sys_pipe_desc_t desc); SP_API sp_err_t sp_sys_pipe_ready_p(sp_sys_fd_t fd, u8* ready); -SP_API sp_err_t sp_sys_mkdir_p(sp_sys_fd_t fd, const c8* path, u32 len, s32 mode); +SP_API sp_err_t sp_sys_mkdir_p(sp_sys_fd_t fd, const c8* path, u32 len, sp_sys_file_perms_t perms); SP_API sp_err_t sp_sys_rmdir_p(sp_sys_fd_t fd, const c8* path, u32 len); SP_API sp_err_t sp_sys_unlink_p(sp_sys_fd_t fd, const c8* path, u32 len); SP_API sp_err_t sp_sys_rename_p(sp_sys_fd_t from_fd, const c8* from, u32 from_len, sp_sys_fd_t to_fd, const c8* to, u32 to_len); @@ -1628,7 +1643,7 @@ SP_API sp_err_t sp_sys_readlink_p(sp_sys_fd_t fd, const c8* path, u32 len, c8 SP_API sp_err_t sp_sys_get_path_metadata_p(sp_sys_fd_t fd, const c8* path, u32 len, sp_sys_file_meta_t* st); SP_API sp_err_t sp_sys_get_link_metadata_p(sp_sys_fd_t fd, const c8* path, u32 len, sp_sys_file_meta_t* st); SP_API sp_err_t sp_sys_get_file_metadata_p(sp_sys_fd_t fd, sp_sys_file_meta_t* st); -SP_API sp_err_t sp_sys_chmod_p(sp_sys_fd_t fd, const c8* path, u32 len, const sp_sys_file_meta_t* st); +SP_API sp_err_t sp_sys_set_file_perms_p(sp_sys_fd_t fd, const c8* path, u32 len, sp_sys_file_perms_t perms); SP_API sp_err_t sp_sys_set_times_p(sp_sys_fd_t fd, const c8* path, u32 len, sp_sys_timespec_t atime, sp_sys_timespec_t mtime); SP_API sp_err_t sp_sys_clock_gettime_p(s32 clockid, sp_sys_timespec_t* ts); SP_API sp_err_t sp_sys_nanosleep_p(const sp_sys_timespec_t* req, sp_sys_timespec_t* rem); @@ -5670,7 +5685,7 @@ const sp_sys_vtable_t sp_sys_vtable_platform = { .get_path_metadata = sp_sys_get_path_metadata_p, .get_link_metadata = sp_sys_get_link_metadata_p, .get_file_metadata = sp_sys_get_file_metadata_p, - .chmod = sp_sys_chmod_p, + .set_file_perms = sp_sys_set_file_perms_p, .set_times = sp_sys_set_times_p, .clock_gettime = sp_sys_clock_gettime_p, .nanosleep = sp_sys_nanosleep_p, @@ -5861,8 +5876,8 @@ sp_err_t sp_sys_pipe_ready(sp_sys_fd_t fd, u8* ready) { return (sp_rt.vt->pipe_ready)(fd, ready); } -sp_err_t sp_sys_mkdir(sp_sys_fd_t fd, const c8* path, u32 len, s32 mode) { - return (sp_rt.vt->mkdir)(fd, path, len, mode); +sp_err_t sp_sys_mkdir(sp_sys_fd_t fd, const c8* path, u32 len, sp_sys_file_perms_t perms) { + return (sp_rt.vt->mkdir)(fd, path, len, perms); } sp_err_t sp_sys_rmdir(sp_sys_fd_t fd, const c8* path, u32 len) { @@ -5901,8 +5916,8 @@ sp_err_t sp_sys_get_file_metadata(sp_sys_fd_t fd, sp_sys_file_meta_t* st) { return (sp_rt.vt->get_file_metadata)(fd, st); } -sp_err_t sp_sys_chmod(sp_sys_fd_t fd, const c8* path, u32 len, const sp_sys_file_meta_t* st) { - return (sp_rt.vt->chmod)(fd, path, len, st); +sp_err_t sp_sys_set_file_perms(sp_sys_fd_t fd, const c8* path, u32 len, sp_sys_file_perms_t perms) { + return (sp_rt.vt->set_file_perms)(fd, path, len, perms); } sp_err_t sp_sys_set_times(sp_sys_fd_t fd, const c8* path, u32 len, sp_sys_timespec_t atime, sp_sys_timespec_t mtime) { @@ -6599,7 +6614,7 @@ static void sp_sys_file_meta_from_statx(const sp_sys_linux_statx_t* raw, sp_sys_ out->id = raw->stx_ino; out->device = ((u64)raw->stx_dev_major << 32) | raw->stx_dev_minor; out->nlink = raw->stx_nlink; - out->raw_attrs = raw->stx_mode; + out->perms.value = raw->stx_mode; } s64 sp_lx_getdents64(s32 fd, void* buf, u64 count) { @@ -6627,7 +6642,7 @@ static void sp_sys_file_meta_from_libc(const struct stat* src, sp_sys_file_meta_ out->id = (u64)src->st_ino; out->device = (u64)src->st_dev; out->nlink = (u64)src->st_nlink; - out->raw_attrs = (u32)src->st_mode; + out->perms.value = (u32)src->st_mode; #if defined(SP_MACOS) out->atime.tv_sec = (s64)src->st_atimespec.tv_sec; out->atime.tv_nsec = (s64)src->st_atimespec.tv_nsec; @@ -7329,7 +7344,7 @@ sp_err_t sp_sys_get_file_metadata_p(sp_sys_fd_t fd, sp_sys_file_meta_t* meta) { meta->id = sp_cast(s64, (id.high | id.low)); meta->device = info.dwVolumeSerialNumber; meta->nlink = info.nNumberOfLinks; - meta->raw_attrs = info.dwFileAttributes; + meta->perms.value = info.dwFileAttributes; sp_sys_timespec_from_filetime(info.ftLastAccessTime, &meta->atime); sp_sys_timespec_from_filetime(info.ftLastWriteTime, &meta->mtime); @@ -8282,7 +8297,7 @@ sp_err_t sp_sys_open_p(sp_sys_fd_t fd, const c8* path, u32 len, sp_sys_open_mode c8 buffer [SP_PATH_MAX]; sp_try(sp_sys_posix_path(path, len, buffer)); - s64 rc = sp_syscall(SP_SYSCALL_NUM_OPENAT, fd, buffer, sp_sys_linux_open_flags(mode, flags), 0644); + s64 rc = sp_syscall(SP_SYSCALL_NUM_OPENAT, fd, buffer, sp_sys_linux_open_flags(mode, flags), sp_sys_default_file_perms.value); if (sp_sys_is_err(rc)) return sp_sys_err_from_errno(-rc); *out = (sp_sys_fd_t)rc; @@ -8291,7 +8306,7 @@ sp_err_t sp_sys_open_p(sp_sys_fd_t fd, const c8* path, u32 len, sp_sys_open_mode #elif defined(SP_MACOS) || defined(SP_COSMO) c8 buf [SP_PATH_MAX]; sp_try(sp_sys_posix_path(path, len, buf)); - s32 rc = openat((int)fd, buf, sp_sys_posix_open_flags(mode, flags), 0644); + s32 rc = openat((int)fd, buf, sp_sys_posix_open_flags(mode, flags), (mode_t)sp_sys_default_file_perms.value); if (rc < 0) return sp_sys_err_from_errno(errno); *out = (sp_sys_fd_t)rc; return SP_OK; @@ -10207,9 +10222,9 @@ sp_err_t sp_sys_get_link_metadata_s(sp_sys_fd_t fd, sp_str_t path, sp_sys_file_m ////////////////// // SP_SYS_MKDIR // ////////////////// -sp_err_t sp_sys_mkdir_p(sp_sys_fd_t fd, const c8* path, u32 len, s32 mode) { +sp_err_t sp_sys_mkdir_p(sp_sys_fd_t fd, const c8* path, u32 len, sp_sys_file_perms_t perms) { #if defined(SP_WIN32) - (void)mode; + (void)perms; sp_sys_fd_t handle = SP_SYS_INVALID_FD; sp_nt_status_t status = sp_sys_nt_open( fd, @@ -10228,15 +10243,15 @@ sp_err_t sp_sys_mkdir_p(sp_sys_fd_t fd, const c8* path, u32 len, s32 mode) { #elif defined(SP_LINUX) c8 buf [SP_PATH_MAX]; sp_try(sp_sys_posix_path(path, len, buf)); - return sp_syscall_e(SP_SYSCALL_NUM_MKDIRAT, fd, buf, mode); + return sp_syscall_e(SP_SYSCALL_NUM_MKDIRAT, fd, buf, perms.value & 07777); #elif defined(SP_MACOS) || defined(SP_COSMO) c8 buf [SP_PATH_MAX]; sp_try(sp_sys_posix_path(path, sp_sys_posix_trim_slashes(path, len), buf)); - return sp_sys_err_from_libc(mkdirat(fd, buf, (mode_t)mode)); + return sp_sys_err_from_libc(mkdirat(fd, buf, (mode_t)(perms.value & 07777))); #elif defined(SP_WASM) - (void)fd; (void)path; (void)len; (void)mode; + (void)fd; (void)path; (void)len; (void)perms; return SP_ERR_SYS_UNSUPPORTED; #else @@ -10244,8 +10259,8 @@ sp_err_t sp_sys_mkdir_p(sp_sys_fd_t fd, const c8* path, u32 len, s32 mode) { #endif } -sp_err_t sp_sys_mkdir_s(sp_sys_fd_t fd, sp_str_t path, s32 mode) { - return sp_sys_mkdir(fd, path.data, path.len, mode); +sp_err_t sp_sys_mkdir_s(sp_sys_fd_t fd, sp_str_t path, sp_sys_file_perms_t perms) { + return sp_sys_mkdir(fd, path.data, path.len, perms); } ////////////////// @@ -10636,10 +10651,10 @@ sp_err_t sp_sys_readlink_s(sp_sys_fd_t fd, sp_str_t path, c8* buf, u64 size, sp_ return err; } -////////////////// -// SP_SYS_CHMOD // -////////////////// -sp_err_t sp_sys_chmod_p(sp_sys_fd_t fd, const c8* path, u32 len, const sp_sys_file_meta_t* st) { +/////////////////////////// +// SP_SYS_SET_FILE_PERMS // +/////////////////////////// +sp_err_t sp_sys_set_file_perms_p(sp_sys_fd_t fd, const c8* path, u32 len, sp_sys_file_perms_t perms) { #if defined(SP_WIN32) sp_sys_fd_t handle = SP_SYS_INVALID_FD; sp_nt_status_t status = sp_sys_nt_open( @@ -10655,7 +10670,7 @@ sp_err_t sp_sys_chmod_p(sp_sys_fd_t fd, const c8* path, u32 len, const sp_sys_fi if (!SP_NT_SUCCESS(status)) return sp_sys_err_from_nt(status); sp_nt_file_basic_information_t info = sp_zero; - info.FileAttributes = st->raw_attrs ? st->raw_attrs : FILE_ATTRIBUTE_NORMAL; + info.FileAttributes = perms.value ? perms.value : FILE_ATTRIBUTE_NORMAL; sp_nt_io_status_block_t iosb = sp_zero; status = SP_NT(NtSetInformationFile)((void*)handle, &iosb, &info, sizeof(info), SP_NT_FILE_BASIC_INFORMATION); @@ -10666,20 +10681,20 @@ sp_err_t sp_sys_chmod_p(sp_sys_fd_t fd, const c8* path, u32 len, const sp_sys_fi #elif defined(SP_LINUX) c8 buf [SP_PATH_MAX]; sp_try(sp_sys_posix_path(path, len, buf)); - s32 mode = (s32)(st->raw_attrs & 07777); + s32 mode = (s32)(perms.value & 07777); return sp_syscall_e(SP_SYSCALL_NUM_FCHMODAT, fd, buf, mode, 0); #elif defined(SP_MACOS) || defined(SP_COSMO) c8 buf [SP_PATH_MAX]; sp_try(sp_sys_posix_path(path, len, buf)); - return sp_sys_err_from_libc(fchmodat(fd, buf, (mode_t)(st->raw_attrs & 07777), 0)); + return sp_sys_err_from_libc(fchmodat(fd, buf, (mode_t)(perms.value & 07777), 0)); #elif defined(SP_WASM) - (void)fd; (void)path; (void)len; (void)st; + (void)fd; (void)path; (void)len; (void)perms; return SP_ERR_SYS_UNSUPPORTED; #else - #error "sp_sys_chmod" + #error "sp_sys_set_file_perms" #endif } @@ -10688,8 +10703,41 @@ sp_err_t sp_sys_set_times_p(sp_sys_fd_t fd, const c8* path, u32 len, sp_sys_time return SP_ERR_SYS_UNSUPPORTED; } -sp_err_t sp_sys_chmod_s(sp_sys_fd_t fd, sp_str_t path, const sp_sys_file_meta_t* st) { - return sp_sys_chmod(fd, path.data, path.len, st); +sp_err_t sp_sys_set_file_perms_s(sp_sys_fd_t fd, sp_str_t path, sp_sys_file_perms_t perms) { + return sp_sys_set_file_perms(fd, path.data, path.len, perms); +} + +bool sp_sys_is_read_only(sp_sys_file_perms_t perms) { +#if defined(SP_WIN32) + return (perms.value & FILE_ATTRIBUTE_READONLY) != 0; +#elif defined(SP_LINUX) || defined(SP_MACOS) || defined(SP_COSMO) || defined(SP_WASM) + return (perms.value & 0222) == 0; +#else + #error "sp_sys_is_read_only" +#endif +} + +void sp_sys_set_read_only(sp_sys_file_perms_t* perms, bool read_only) { +#if defined(SP_WIN32) + if (read_only) perms->value |= FILE_ATTRIBUTE_READONLY; + else perms->value &= ~(u32)FILE_ATTRIBUTE_READONLY; +#elif defined(SP_LINUX) || defined(SP_MACOS) || defined(SP_COSMO) || defined(SP_WASM) + if (read_only) perms->value &= ~(u32)0222; + else perms->value |= 0200; +#else + #error "sp_sys_set_read_only" +#endif +} + +void sp_sys_set_executable(sp_sys_file_perms_t* perms, bool executable) { +#if defined(SP_WIN32) + (void)perms; (void)executable; +#elif defined(SP_LINUX) || defined(SP_MACOS) || defined(SP_COSMO) || defined(SP_WASM) + if (executable) perms->value |= (perms->value & 0444) >> 2; + else perms->value &= ~(u32)0111; +#else + #error "sp_sys_set_executable" +#endif } sp_err_t sp_sys_set_times_s(sp_sys_fd_t fd, sp_str_t path, sp_sys_timespec_t atime, sp_sys_timespec_t mtime) { @@ -19957,7 +20005,7 @@ sp_err_t sp_io_read_file(sp_mem_t mem, sp_str_t path, sp_str_t* content) { SP_PRIVATE sp_err_t sp_fs_create_parent_at(sp_sys_fd_t dir, sp_str_t path); SP_PRIVATE sp_err_t sp_fs_mkdir_at(sp_sys_fd_t dir, sp_str_t path) { - sp_err_t err = sp_sys_mkdir_s(dir, path, 0755); + sp_err_t err = sp_sys_mkdir_s(dir, path, sp_sys_default_dir_perms); if (!err || err == SP_ERR_SYS_NOT_FOUND) return err; sp_sys_file_meta_t st = sp_zero; @@ -20344,7 +20392,7 @@ sp_err_t sp_fs_write_atomic_cstr(sp_str_t path, const c8* str) { return sp_fs_write_atomic(path, sp_str_view(str)); } -SP_PRIVATE sp_err_t sp_fs_copy_bytes(sp_str_t from, sp_str_t to, sp_fs_atomic_mode_t mode, const sp_sys_file_meta_t* meta) { +SP_PRIVATE sp_err_t sp_fs_copy_bytes(sp_str_t from, sp_str_t to, sp_fs_atomic_mode_t mode, sp_sys_file_perms_t perms) { sp_io_file_reader_t reader = sp_zero; sp_try(sp_io_file_reader_from_path(&reader, from)); @@ -20357,7 +20405,7 @@ SP_PRIVATE sp_err_t sp_fs_copy_bytes(sp_str_t from, sp_str_t to, sp_fs_atomic_mo err = sp_io_copy(sp_fs_atomic_writer(&af), &reader.base, SP_NULLPTR); sp_io_file_reader_close(&reader); - if (!err) err = sp_sys_chmod_s(af.dir, af.temp, meta); + if (!err) err = sp_sys_set_file_perms_s(af.dir, af.temp, perms); if (err) { sp_fs_atomic_abort(&af); return err; @@ -20368,7 +20416,7 @@ SP_PRIVATE sp_err_t sp_fs_copy_bytes(sp_str_t from, sp_str_t to, sp_fs_atomic_mo SP_PRIVATE sp_err_t sp_fs_copy_tree_file(sp_str_t from, sp_str_t to, sp_fs_atomic_mode_t mode) { sp_sys_file_meta_t meta = sp_zero; sp_try(sp_sys_get_path_metadata_s(sp_sys_get_root(0), from, &meta)); - return sp_fs_copy_bytes(from, to, mode, &meta); + return sp_fs_copy_bytes(from, to, mode, meta.perms); } SP_PRIVATE sp_err_t sp_fs_copy_link(sp_str_t from, sp_str_t to, sp_fs_atomic_mode_t mode) { @@ -20399,7 +20447,7 @@ sp_err_t sp_fs_copy_file(sp_str_t from, sp_str_t to, sp_fs_atomic_mode_t mode) { sp_try(sp_sys_get_path_metadata_s(sp_sys_get_root(0), from, &meta)); switch (meta.kind) { - case SP_FS_KIND_FILE: return sp_fs_copy_bytes(from, to, mode, &meta); + case SP_FS_KIND_FILE: return sp_fs_copy_bytes(from, to, mode, meta.perms); case SP_FS_KIND_DIR: return SP_ERR_SYS_IS_DIR; case SP_FS_KIND_SYMLINK: case SP_FS_KIND_NONE: return SP_ERR_SYS_UNSUPPORTED; @@ -20485,7 +20533,7 @@ sp_err_t sp_fs_copy(sp_str_t from, sp_str_t to) { sp_try(sp_sys_get_path_metadata_s(sp_sys_get_root(0), from, &meta)); switch (meta.kind) { - case SP_FS_KIND_FILE: return sp_fs_copy_bytes(from, to, SP_FS_ATOMIC_REPLACE, &meta); + case SP_FS_KIND_FILE: return sp_fs_copy_bytes(from, to, SP_FS_ATOMIC_REPLACE, meta.perms); case SP_FS_KIND_DIR: return sp_fs_copy_tree(from, to, SP_FS_ATOMIC_REPLACE); case SP_FS_KIND_SYMLINK: case SP_FS_KIND_NONE: return SP_ERR_SYS_UNSUPPORTED; diff --git a/spn.toml b/spn.toml index 732d2ee..99ae3a9 100644 --- a/spn.toml +++ b/spn.toml @@ -16,7 +16,7 @@ source = [ "test/test/axis.c", "test/test/filter.c", "test/test/matrix.c", "test/test/resolve.c", "test/test/sweep.c", "test/test/tracking.c", "test/sys/sys.c", "test/sys/open.c", "test/sys/path.c", "test/sys/iter.c", "test/sys/futex.c", - "test/sys/fd.c", "test/sys/nanosleep.c", "test/sys/timestamps.c", "test/sys/self_path.c", + "test/sys/fd.c", "test/sys/nanosleep.c", "test/sys/timestamps.c", "test/sys/perms.c", "test/sys/self_path.c", "test/sys/pipe/data.c", "test/sys/pipe/blocking.c", "test/sys/transfer.c", "test/sys/readlink.c", "test/sys/socket/data.c", "test/sys/socket/listen.c", "test/sys/socket/desc.c", "test/sys/tty/contract.c", "test/sys/tty/mode.c", "test/sys/vtable.c", @@ -39,7 +39,7 @@ name = "sys" source = [ "test/main.c", "test/sys/sys.c", "test/sys/open.c", "test/sys/path.c", "test/sys/iter.c", "test/sys/futex.c", - "test/sys/fd.c", "test/sys/nanosleep.c", "test/sys/timestamps.c", "test/sys/self_path.c", + "test/sys/fd.c", "test/sys/nanosleep.c", "test/sys/timestamps.c", "test/sys/perms.c", "test/sys/self_path.c", "test/sys/pipe/data.c", "test/sys/pipe/blocking.c", "test/sys/transfer.c", "test/sys/readlink.c", "test/sys/socket/data.c", "test/sys/socket/listen.c", "test/sys/socket/desc.c", "test/sys/tty/contract.c", "test/sys/tty/mode.c", "test/sys/vtable.c", diff --git a/test/sys/path.c b/test/sys/path.c index 4b67c2b..326c7d0 100644 --- a/test/sys/path.c +++ b/test/sys/path.c @@ -417,7 +417,7 @@ static sp_err_t run(sp_test_t* t, test_t* c) { switch (c->op) { case OP_UNLINK: err = sp_sys_unlink_s(sandbox_fd, path); break; case OP_RMDIR: err = sp_sys_rmdir_s(sandbox_fd, path); break; - case OP_MKDIR: err = sp_sys_mkdir_s(sandbox_fd, path, 0755); break; + case OP_MKDIR: err = sp_sys_mkdir_s(sandbox_fd, path, sp_sys_default_dir_perms); break; case OP_STAT: err = sp_sys_get_path_metadata_s(sandbox_fd, path, &meta); break; case OP_LSTAT: err = sp_sys_get_link_metadata_s(sandbox_fd, path, &meta); break; } @@ -489,9 +489,8 @@ sp_test(sys, canonicalize_ignores_read_permission) { if (geteuid() == 0) return sp_test_skip(t, "running as root"); #endif - sp_sys_file_meta_t meta = sp_zero; - sp_err_t err = sp_sys_chmod_s(sp_sys_get_root(0), path, &meta); - if (err == SP_ERR_SYS_UNSUPPORTED) return sp_test_skip(t, "chmod not supported"); + sp_err_t err = sp_sys_set_file_perms_s(sp_sys_get_root(0), path, sp_zero_s(sp_sys_file_perms_t)); + if (err == SP_ERR_SYS_UNSUPPORTED) return sp_test_skip(t, "set_file_perms not supported"); sp_try(err); c8 buf [SP_PATH_MAX] = sp_zero; diff --git a/test/sys/perms.c b/test/sys/perms.c new file mode 100644 index 0000000..01af08f --- /dev/null +++ b/test/sys/perms.c @@ -0,0 +1,105 @@ +#include "sp.h" +#include "sp/sp_test.h" + +#define PERMS_TEST_MAX_OPS 2 + +typedef enum { + OP_NONE, + OP_READ_ONLY, + OP_EXECUTABLE, +} op_kind_t; + +typedef struct { + op_kind_t kind; + bool value; +} op_t; + +typedef struct { + bool read_only; + bool executable; +} expect_t; + +typedef struct { + const c8* name; + op_t ops [PERMS_TEST_MAX_OPS]; + expect_t expect; +} test_t; + +static const test_t tests [] = { + { .name = "fresh_file_is_writable" }, + { .name = "set_read_only", .ops = { { OP_READ_ONLY, true } }, .expect = { .read_only = true } }, + { .name = "clear_read_only", .ops = { { OP_READ_ONLY, true }, { OP_READ_ONLY, false } } }, + { .name = "set_executable", .ops = { { OP_EXECUTABLE, true } }, .expect = { .executable = true } }, + { .name = "clear_executable", .ops = { { OP_EXECUTABLE, true }, { OP_EXECUTABLE, false } } }, + { .name = "read_only_keeps_executable", .ops = { { OP_EXECUTABLE, true }, { OP_READ_ONLY, true } }, .expect = { .read_only = true, .executable = true } }, +}; + +static sp_err_t run(sp_test_t* t, test_t* it) { + sp_str_t path = sp_fs_join_path(sp_test_arena(t), sp_test_dir(t), sp_str_lit("A")); + sp_fs_create_file(path); + sp_sys_fd_t root = sp_sys_get_root(0); + + sp_sys_file_meta_t meta = sp_zero; + sp_err_t err = sp_sys_get_path_metadata_s(root, path, &meta); + if (err == SP_ERR_SYS_UNSUPPORTED) return sp_test_skip(t, "metadata not supported"); + sp_try(err); + + sp_carr_for_until(it->ops, i, it->ops[i].kind != OP_NONE) { + op_t op = it->ops[i]; + switch (op.kind) { + case OP_NONE: break; + case OP_READ_ONLY: sp_sys_set_read_only(&meta.perms, op.value); break; + case OP_EXECUTABLE: sp_sys_set_executable(&meta.perms, op.value); break; + } + } + sp_must_ok(t, sp_sys_set_file_perms_s(root, path, meta.perms)); + + sp_sys_file_meta_t after = sp_zero; + sp_must_ok(t, sp_sys_get_path_metadata_s(root, path, &after)); + sp_expect_eq(t, sp_sys_is_read_only(after.perms), it->expect.read_only); +#if !defined(SP_WIN32) + sp_expect_eq(t, (after.perms.value & 0111) != 0, it->expect.executable); +#endif + return SP_OK; +} + +sp_test_each_fn(sys, perms, test_t, tests, run); + +sp_test(sys, default_perms_then_read_only) { + sp_str_t path = sp_fs_join_path(sp_test_arena(t), sp_test_dir(t), sp_str_lit("A")); + sp_fs_create_file(path); + sp_sys_fd_t root = sp_sys_get_root(0); + + sp_err_t err = sp_sys_set_file_perms_s(root, path, sp_sys_default_file_perms); + if (err == SP_ERR_SYS_UNSUPPORTED) return sp_test_skip(t, "set_file_perms not supported"); + sp_try(err); + + sp_sys_file_meta_t meta = sp_zero; + sp_must_ok(t, sp_sys_get_path_metadata_s(root, path, &meta)); + sp_expect(t, !sp_sys_is_read_only(meta.perms)); + + sp_sys_set_read_only(&meta.perms, true); + sp_must_ok(t, sp_sys_set_file_perms_s(root, path, meta.perms)); + + sp_sys_file_meta_t after = sp_zero; + sp_must_ok(t, sp_sys_get_path_metadata_s(root, path, &after)); + sp_expect(t, sp_sys_is_read_only(after.perms)); + return SP_OK; +} + +sp_test(sys, mkdir_applies_perms) { + sp_test_skip_on_win32(); + sp_str_t path = sp_fs_join_path(sp_test_arena(t), sp_test_dir(t), sp_str_lit("D")); + sp_sys_fd_t root = sp_sys_get_root(0); + + sp_sys_file_perms_t perms = sp_sys_default_dir_perms; + sp_sys_set_read_only(&perms, true); + sp_err_t err = sp_sys_mkdir_s(root, path, perms); + if (err == SP_ERR_SYS_UNSUPPORTED) return sp_test_skip(t, "mkdir not supported"); + sp_try(err); + + sp_sys_file_meta_t meta = sp_zero; + sp_must_ok(t, sp_sys_get_path_metadata_s(root, path, &meta)); + sp_expect(t, sp_sys_is_read_only(meta.perms)); + return SP_OK; +} diff --git a/test/sys/sys.h b/test/sys/sys.h index 6c884ca..ea9afac 100644 --- a/test/sys/sys.h +++ b/test/sys/sys.h @@ -189,13 +189,9 @@ static bool sys_apply_setup(s32* utest_result, sp_test_file_manager_t* fm, sp_st SP_FAIL(); return false; } -#if defined(SP_WIN32) - meta.raw_attrs |= FILE_ATTRIBUTE_READONLY; -#else - meta.raw_attrs &= ~(u32)0222; -#endif - if (sp_sys_chmod_s(sp_sys_get_root(0), path, &meta)) { - SP_TEST_REPORT("failed to chmod {}", sp_fmt_str(path)); + sp_sys_set_read_only(&meta.perms, true); + if (sp_sys_set_file_perms_s(sp_sys_get_root(0), path, meta.perms)) { + SP_TEST_REPORT("failed to set perms on {}", sp_fmt_str(path)); SP_FAIL(); return false; } @@ -376,7 +372,7 @@ static void run_sys_test(s32* utest_result, sys_test_t t) { break; } case SYS_STEP_MKDIR: { - sp_err_t err = sp_sys_mkdir_s(sandbox_fd, sp_cstr_as_str(step->mkdir.path), 0755); + sp_err_t err = sp_sys_mkdir_s(sandbox_fd, sp_cstr_as_str(step->mkdir.path), sp_sys_default_dir_perms); sys_expect_err(utest_result, "mkdir", err, step->mkdir.err); break; } diff --git a/test/sys/timestamps.c b/test/sys/timestamps.c index a5f1d05..a2af087 100644 --- a/test/sys/timestamps.c +++ b/test/sys/timestamps.c @@ -8,7 +8,7 @@ typedef enum { typedef enum { OP_WRITE, - OP_CHMOD, + OP_SET_PERMS, } op_t; typedef struct { @@ -20,9 +20,9 @@ typedef struct { static const test_t tests [] = { { .name = "write_moves_mtime", .field = FIELD_MTIME, .op = OP_WRITE, .moves = true }, - { .name = "chmod_keeps_mtime", .field = FIELD_MTIME, .op = OP_CHMOD }, + { .name = "set_perms_keeps_mtime", .field = FIELD_MTIME, .op = OP_SET_PERMS }, { .name = "write_keeps_btime", .field = FIELD_BTIME, .op = OP_WRITE }, - { .name = "chmod_keeps_btime", .field = FIELD_BTIME, .op = OP_CHMOD }, + { .name = "set_perms_keeps_btime", .field = FIELD_BTIME, .op = OP_SET_PERMS }, }; static s64 field_ns(const sp_sys_file_meta_t* meta, field_t field) { @@ -52,9 +52,10 @@ static sp_err_t run(sp_test_t* t, test_t* c) { sp_sys_close(fd); break; } - case OP_CHMOD: { - sp_sys_file_meta_t meta = sp_zero; - sp_must_ok(t, sp_sys_chmod_s(root, path, &meta)); + case OP_SET_PERMS: { + sp_sys_file_perms_t perms = before.perms; + sp_sys_set_read_only(&perms, true); + sp_must_ok(t, sp_sys_set_file_perms_s(root, path, perms)); break; } } diff --git a/test/sys/vtable.c b/test/sys/vtable.c index d7906bb..4faf9c4 100644 --- a/test/sys/vtable.c +++ b/test/sys/vtable.c @@ -75,7 +75,7 @@ static sp_err_t mock_pipe_ready(sp_sys_fd_t fd, u8* ready) { return (sp_err_t)69; } -static sp_err_t mock_mkdir(sp_sys_fd_t fd, const c8* path, u32 len, s32 mode) { +static sp_err_t mock_mkdir(sp_sys_fd_t fd, const c8* path, u32 len, sp_sys_file_perms_t perms) { return (sp_err_t)69; } @@ -115,7 +115,7 @@ static sp_err_t mock_get_file_metadata(sp_sys_fd_t fd, sp_sys_file_meta_t* st) { return (sp_err_t)69; } -static sp_err_t mock_chmod(sp_sys_fd_t fd, const c8* path, u32 len, const sp_sys_file_meta_t* st) { +static sp_err_t mock_set_file_perms(sp_sys_fd_t fd, const c8* path, u32 len, sp_sys_file_perms_t perms) { return (sp_err_t)69; } @@ -326,7 +326,7 @@ static const sp_sys_vtable_t mock = { .get_path_metadata = mock_get_path_metadata, .get_link_metadata = mock_get_link_metadata, .get_file_metadata = mock_get_file_metadata, - .chmod = mock_chmod, + .set_file_perms = mock_set_file_perms, .clock_gettime = mock_clock_gettime, .nanosleep = mock_nanosleep, .futex_wait = mock_futex_wait, @@ -444,7 +444,7 @@ static s64 call_pipe_ready(void) { } static s64 call_mkdir(void) { - return (s64)sp_sys_mkdir(0, SP_NULLPTR, 0, 0); + return (s64)sp_sys_mkdir(0, SP_NULLPTR, 0, sp_zero_s(sp_sys_file_perms_t)); } static s64 call_rmdir(void) { @@ -484,8 +484,8 @@ static s64 call_get_file_metadata(void) { return (s64)sp_sys_get_file_metadata(0, SP_NULLPTR); } -static s64 call_chmod(void) { - return (s64)sp_sys_chmod(0, SP_NULLPTR, 0, SP_NULLPTR); +static s64 call_set_file_perms(void) { + return (s64)sp_sys_set_file_perms(0, SP_NULLPTR, 0, sp_zero_s(sp_sys_file_perms_t)); } static s64 call_clock_gettime(void) { @@ -709,7 +709,7 @@ static const test_t tests [] = { { "get_path_metadata", call_get_path_metadata, 69 }, { "get_link_metadata", call_get_link_metadata, 69 }, { "get_file_metadata", call_get_file_metadata, 69 }, - { "chmod", call_chmod, 69 }, + { "set_file_perms", call_set_file_perms, 69 }, { "clock_gettime", call_clock_gettime, 69 }, { "nanosleep", call_nanosleep, 69 }, { "futex_wait", call_futex_wait, 1 },