Wrap file metadata calls #31
@@ -76,6 +76,7 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
logikoma marked this conversation as resolved
|
|||||||
#include <sys/statvfs.h>
|
#include <sys/statvfs.h>
|
||||||
/* off_t, for the aksl_fseeko/aksl_ftello pair. POSIX, like aksl_realpath. */
|
/* off_t, for the aksl_fseeko/aksl_ftello pair. POSIX, like aksl_realpath. */
|
||||||
@@ -810,10 +811,25 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_strhash_djb2_str(const char *str, uint32
|
|||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
|
/* ====================================================================== */
|
||||||
|
/** @name File and filesystem metadata
|
||||||
|
*
|
||||||
|
* The stat family reports into caller-owned POSIX structs rather than a smaller
|
||||||
|
* library-defined copy: the platform owns the fields, and a wrapper should not
|
||||||
|
* discard a field merely because this library does not currently use it. libc
|
||||||
|
* failures retain their errno value as the status, so callers can distinguish
|
||||||
|
* absent paths from inaccessible ones without parsing an error message.
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
/* ====================================================================== */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief stat(2).
|
* @brief stat(2).
|
||||||
* @param[in] pathname Path to inspect. Required.
|
* @param[in] pathname Path to inspect. Required.
|
||||||
* @param[out] dest File metadata. Required.
|
* @param[out] dest File metadata. Required.
|
||||||
|
* @throws AKERR_NULLPOINTER If pathname or dest is NULL.
|
||||||
|
* @throws AKERR_IO If stat(2) failed and left errno at 0.
|
||||||
|
* @throws (errno) The errno stat(2) set, reported directly as the status.
|
||||||
* @return NULL on success, an error context otherwise.
|
* @return NULL on success, an error context otherwise.
|
||||||
*/
|
*/
|
||||||
akerr_ErrorContext AKERR_NOIGNORE *aksl_stat(const char *pathname, struct stat *dest);
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_stat(const char *pathname, struct stat *dest);
|
||||||
@@ -822,6 +838,9 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_stat(const char *pathname, struct stat *
|
|||||||
* @brief lstat(2), inspecting a symbolic link itself.
|
* @brief lstat(2), inspecting a symbolic link itself.
|
||||||
* @param[in] pathname Path to inspect. Required.
|
* @param[in] pathname Path to inspect. Required.
|
||||||
* @param[out] dest File metadata. Required.
|
* @param[out] dest File metadata. Required.
|
||||||
|
* @throws AKERR_NULLPOINTER If pathname or dest is NULL.
|
||||||
|
* @throws AKERR_IO If lstat(2) failed and left errno at 0.
|
||||||
|
* @throws (errno) The errno lstat(2) set, reported directly as the status.
|
||||||
* @return NULL on success, an error context otherwise.
|
* @return NULL on success, an error context otherwise.
|
||||||
*/
|
*/
|
||||||
akerr_ErrorContext AKERR_NOIGNORE *aksl_lstat(const char *pathname, struct stat *dest);
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_lstat(const char *pathname, struct stat *dest);
|
||||||
@@ -830,6 +849,9 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_lstat(const char *pathname, struct stat
|
|||||||
* @brief fstat(2).
|
* @brief fstat(2).
|
||||||
* @param[in] fd Open file descriptor.
|
* @param[in] fd Open file descriptor.
|
||||||
* @param[out] dest File metadata. Required.
|
* @param[out] dest File metadata. Required.
|
||||||
|
* @throws AKERR_NULLPOINTER If dest is NULL.
|
||||||
|
* @throws AKERR_IO If fstat(2) failed and left errno at 0.
|
||||||
|
* @throws (errno) The errno fstat(2) set, reported directly as the status.
|
||||||
* @return NULL on success, an error context otherwise.
|
* @return NULL on success, an error context otherwise.
|
||||||
*/
|
*/
|
||||||
akerr_ErrorContext AKERR_NOIGNORE *aksl_fstat(int fd, struct stat *dest);
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_fstat(int fd, struct stat *dest);
|
||||||
@@ -840,6 +862,9 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_fstat(int fd, struct stat *dest);
|
|||||||
* @param[in] pathname Path to inspect. Required.
|
* @param[in] pathname Path to inspect. Required.
|
||||||
* @param[out] dest File metadata. Required.
|
* @param[out] dest File metadata. Required.
|
||||||
* @param[in] flags libc fstatat flags, passed unchanged.
|
* @param[in] flags libc fstatat flags, passed unchanged.
|
||||||
|
* @throws AKERR_NULLPOINTER If pathname or dest is NULL.
|
||||||
|
* @throws AKERR_IO If fstatat(2) failed and left errno at 0.
|
||||||
|
* @throws (errno) The errno fstatat(2) set, reported directly as the status.
|
||||||
* @return NULL on success, an error context otherwise.
|
* @return NULL on success, an error context otherwise.
|
||||||
*/
|
*/
|
||||||
akerr_ErrorContext AKERR_NOIGNORE *aksl_fstatat(int dirfd, const char *pathname, struct stat *dest, int flags);
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_fstatat(int dirfd, const char *pathname, struct stat *dest, int flags);
|
||||||
@@ -848,6 +873,9 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_fstatat(int dirfd, const char *pathname,
|
|||||||
* @brief statvfs(3).
|
* @brief statvfs(3).
|
||||||
* @param[in] path Path on the filesystem. Required.
|
* @param[in] path Path on the filesystem. Required.
|
||||||
* @param[out] dest Filesystem metadata. Required.
|
* @param[out] dest Filesystem metadata. Required.
|
||||||
|
* @throws AKERR_NULLPOINTER If path or dest is NULL.
|
||||||
|
* @throws AKERR_IO If statvfs(3) failed and left errno at 0.
|
||||||
|
* @throws (errno) The errno statvfs(3) set, reported directly as the status.
|
||||||
* @return NULL on success, an error context otherwise.
|
* @return NULL on success, an error context otherwise.
|
||||||
*/
|
*/
|
||||||
akerr_ErrorContext AKERR_NOIGNORE *aksl_statvfs(const char *path, struct statvfs *dest);
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_statvfs(const char *path, struct statvfs *dest);
|
||||||
@@ -856,11 +884,15 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_statvfs(const char *path, struct statvfs
|
|||||||
* @brief fstatvfs(3).
|
* @brief fstatvfs(3).
|
||||||
* @param[in] fd Open file descriptor.
|
* @param[in] fd Open file descriptor.
|
||||||
* @param[out] dest Filesystem metadata. Required.
|
* @param[out] dest Filesystem metadata. Required.
|
||||||
|
* @throws AKERR_NULLPOINTER If dest is NULL.
|
||||||
|
* @throws AKERR_IO If fstatvfs(3) failed and left errno at 0.
|
||||||
|
* @throws (errno) The errno fstatvfs(3) set, reported directly as the status.
|
||||||
* @return NULL on success, an error context otherwise.
|
* @return NULL on success, an error context otherwise.
|
||||||
*/
|
*/
|
||||||
akerr_ErrorContext AKERR_NOIGNORE *aksl_fstatvfs(int fd, struct statvfs *dest);
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_fstatvfs(int fd, struct statvfs *dest);
|
||||||
|
|
||||||
|
|
||||||
|
/** @} */
|
||||||
/* ====================================================================== */
|
/* ====================================================================== */
|
||||||
/** @name Streams: open, read, write, close
|
/** @name Streams: open, read, write, close
|
||||||
*
|
*
|
||||||
|
|||||||
32
src/stat.c
@@ -1,6 +1,20 @@
|
|||||||
/* sys/stat.h and sys/statvfs.h metadata wrappers. */
|
/*
|
||||||
|
* sys/stat.h and sys/statvfs.h metadata wrappers.
|
||||||
|
*
|
||||||
|
* These calls make information about a file or filesystem available only by a
|
||||||
|
* return value that must be checked alongside a caller-owned POSIX struct. The
|
||||||
|
* wrappers put failure in the return value, where AKERR_NOIGNORE prevents it
|
||||||
|
* from being silently dropped, while preserving the errno that distinguishes
|
||||||
|
* missing paths, inaccessible paths, and invalid descriptors. errno is cleared
|
||||||
|
* immediately before each libc call, so a broken libc that reports failure
|
||||||
|
* without setting errno is still an AKERR_IO failure rather than status 0.
|
||||||
|
*/
|
||||||
#include <akstdlib.h>
|
#include <akstdlib.h>
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/statvfs.h>
|
||||||
|
|
||||||
#include "aksl_internal.h"
|
#include "aksl_internal.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -41,8 +55,11 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_lstat(const char *pathname, struct stat
|
|||||||
*/
|
*/
|
||||||
akerr_ErrorContext AKERR_NOIGNORE *aksl_fstat(int fd, struct stat *dest)
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_fstat(int fd, struct stat *dest)
|
||||||
{
|
{
|
||||||
PREPARE_ERROR(e); FAIL_ZERO_RETURN(e, dest, AKERR_NULLPOINTER, "fd=%d, dest=%p", fd, (void *)dest);
|
PREPARE_ERROR(e);
|
||||||
errno = 0; FAIL_NONZERO_RETURN(e, fstat(fd, dest), AKSL_ERRNO_OR(AKERR_IO), "fd=%d", fd); SUCCEED_RETURN(e);
|
FAIL_ZERO_RETURN(e, dest, AKERR_NULLPOINTER, "fd=%d, dest=%p", fd, (void *)dest);
|
||||||
|
errno = 0;
|
||||||
|
FAIL_NONZERO_RETURN(e, fstat(fd, dest), AKSL_ERRNO_OR(AKERR_IO), "fd=%d", fd);
|
||||||
|
SUCCEED_RETURN(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -57,7 +74,7 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_fstatat(int dirfd, const char *pathname,
|
|||||||
FAIL_ZERO_RETURN(e, pathname, AKERR_NULLPOINTER, "dirfd=%d, pathname=%p, dest=%p", dirfd, (void *)pathname, (void *)dest);
|
FAIL_ZERO_RETURN(e, pathname, AKERR_NULLPOINTER, "dirfd=%d, pathname=%p, dest=%p", dirfd, (void *)pathname, (void *)dest);
|
||||||
FAIL_ZERO_RETURN(e, dest, AKERR_NULLPOINTER, "dirfd=%d, pathname=%p, dest=%p", dirfd, (void *)pathname, (void *)dest);
|
FAIL_ZERO_RETURN(e, dest, AKERR_NULLPOINTER, "dirfd=%d, pathname=%p, dest=%p", dirfd, (void *)pathname, (void *)dest);
|
||||||
|
logikoma marked this conversation as resolved
tachikoma
commented
DEFECT (high) — Third of the three. gcov, full suite: Fix, at **DEFECT (high) — `aksl_statvfs`'s failure branch is never executed.**
Third of the three. gcov, full suite: `taken 0%`, `FAIL` body `never executed`. `tests/test_stat.c:69` is the only non-NULL call and it is the success path.
**Fix, at `tests/test_stat.c:70`:**
```c
AKSL_CHECK_STATUS(aksl_statvfs("/nonexistent/aksl/stat", &vdest), ENOENT);
```
|
|||||||
errno = 0;
|
errno = 0;
|
||||||
FAIL_NONZERO_RETURN(e, fstatat(dirfd, pathname, dest, flags), AKSL_ERRNO_OR(AKERR_IO), "dirfd=%d pathname=%s flags=%d", dirfd, pathname, flags);
|
FAIL_NONZERO_RETURN(e, fstatat(dirfd, pathname, dest, flags), AKSL_ERRNO_OR(AKERR_IO), "dirfd=%d, pathname=%s, flags=0x%x", dirfd, pathname, flags);
|
||||||
SUCCEED_RETURN(e);
|
SUCCEED_RETURN(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,6 +100,9 @@ akerr_ErrorContext AKERR_NOIGNORE *aksl_statvfs(const char *path, struct statvfs
|
|||||||
*/
|
*/
|
||||||
akerr_ErrorContext AKERR_NOIGNORE *aksl_fstatvfs(int fd, struct statvfs *dest)
|
akerr_ErrorContext AKERR_NOIGNORE *aksl_fstatvfs(int fd, struct statvfs *dest)
|
||||||
{
|
{
|
||||||
PREPARE_ERROR(e); FAIL_ZERO_RETURN(e, dest, AKERR_NULLPOINTER, "fd=%d, dest=%p", fd, (void *)dest);
|
PREPARE_ERROR(e);
|
||||||
errno = 0; FAIL_NONZERO_RETURN(e, fstatvfs(fd, dest), AKSL_ERRNO_OR(AKERR_IO), "fd=%d", fd); SUCCEED_RETURN(e);
|
FAIL_ZERO_RETURN(e, dest, AKERR_NULLPOINTER, "fd=%d, dest=%p", fd, (void *)dest);
|
||||||
|
errno = 0;
|
||||||
|
FAIL_NONZERO_RETURN(e, fstatvfs(fd, dest), AKSL_ERRNO_OR(AKERR_IO), "fd=%d", fd);
|
||||||
|
SUCCEED_RETURN(e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,14 @@
|
|||||||
#include "aksl_capture.h"
|
#include "aksl_capture.h"
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A bit libc has never assigned in the AT_ space. fstatat(2) must reject it
|
||||||
|
* with EINVAL rather than ignoring it, which is what proves flags reach libc
|
||||||
|
* unmasked. Re-check this constant if AT_ ever grows into 0x40000000.
|
||||||
|
*/
|
||||||
|
#define AKSL_TEST_AT_INVALID 0x40000000
|
||||||
|
|
||||||
static int test_stat_success_and_fstat(void)
|
static int test_stat_success_and_fstat(void)
|
||||||
{
|
{
|
||||||
@@ -30,6 +38,7 @@ static int test_stat_success_and_fstat(void)
|
|||||||
|
|
||||||
/* A descriptor that was just closed must surface libc's EBADF. */
|
/* A descriptor that was just closed must surface libc's EBADF. */
|
||||||
AKSL_CHECK_STATUS(aksl_fstat(fd, &fd_dest), EBADF);
|
AKSL_CHECK_STATUS(aksl_fstat(fd, &fd_dest), EBADF);
|
||||||
|
AKSL_CHECK_STATUS(aksl_fstatvfs(fd, &vfs_dest), EBADF);
|
||||||
AKSL_CHECK(unlink(path) == 0);
|
AKSL_CHECK(unlink(path) == 0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -38,15 +47,21 @@ static int test_stat_paths_and_fstatat(void)
|
|||||||
{
|
{
|
||||||
char path[AKSL_TMP_MAX];
|
char path[AKSL_TMP_MAX];
|
||||||
char child[AKSL_TMP_MAX * 2];
|
char child[AKSL_TMP_MAX * 2];
|
||||||
|
char directory[AKSL_TMP_MAX];
|
||||||
char linkpath[AKSL_TMP_MAX * 2];
|
char linkpath[AKSL_TMP_MAX * 2];
|
||||||
|
char *basename = NULL;
|
||||||
|
int dirfd = -1;
|
||||||
struct stat dest;
|
struct stat dest;
|
||||||
struct stat ldest;
|
struct stat ldest;
|
||||||
|
struct stat path_dest;
|
||||||
struct statvfs vdest;
|
struct statvfs vdest;
|
||||||
/* Missing paths and a regular file used as a directory preserve errno. */
|
/* Missing paths and a regular file used as a directory preserve errno. */
|
||||||
AKSL_CHECK_STATUS(aksl_stat("/nonexistent/aksl/stat", &dest), ENOENT);
|
AKSL_CHECK_STATUS(aksl_stat("/nonexistent/aksl/stat", &dest), ENOENT);
|
||||||
|
AKSL_CHECK_STATUS(aksl_lstat("/nonexistent/aksl/stat", &ldest), ENOENT);
|
||||||
/* The temporary path is a regular file, so adding a child tests ENOTDIR. */
|
/* The temporary path is a regular file, so adding a child tests ENOTDIR. */
|
||||||
AKSL_CHECK(aksl_temp_file(path, sizeof(path)) == 0);
|
AKSL_CHECK(aksl_temp_file(path, sizeof(path)) == 0);
|
||||||
AKSL_CHECK(snprintf(child, sizeof(child), "%s/child", path) < (int)sizeof(child));
|
AKSL_CHECK(snprintf(child, sizeof(child), "%s/child", path) < (int)sizeof(child));
|
||||||
|
AKSL_CHECK_OK(aksl_stat(path, &path_dest));
|
||||||
AKSL_CHECK_STATUS(aksl_stat(child, &dest), ENOTDIR);
|
AKSL_CHECK_STATUS(aksl_stat(child, &dest), ENOTDIR);
|
||||||
|
logikoma marked this conversation as resolved
tachikoma
commented
Nit — magic number.
Fix: name it and say why. **Nit — magic number.**
`0x40000000` is "a flag bit libc will reject", and nothing in the file says so. It is currently unallocated on Linux, but the `AT_` space is still being handed out — `AT_RECURSIVE` (0x8000), the `AT_STATX_*` bits and `AT_HANDLE_FID` were all added over time. When 0x40000000 is eventually assigned, this test either starts failing for a reason nobody will connect to this line, or worse, keeps passing for a different reason.
**Fix:** name it and say why.
```c
/*
* A bit libc has never assigned in the AT_ space. fstatat(2) must reject it
* with EINVAL rather than ignoring it, which is what proves flags reach libc
* unmasked. Re-check this constant if AT_ ever grows into 0x40000000.
*/
#define AKSL_TEST_AT_INVALID 0x40000000
```
|
|||||||
AKSL_CHECK(snprintf(linkpath, sizeof(linkpath), "%s.link", path) < (int)sizeof(linkpath));
|
AKSL_CHECK(snprintf(linkpath, sizeof(linkpath), "%s.link", path) < (int)sizeof(linkpath));
|
||||||
|
logikoma marked this conversation as resolved
tachikoma
commented
DEFECT (medium) — this asserts on
C permits a standard library function to set Two further problems with it:
Fix: delete line 66. The Worth stating the other half plainly, since it is the thing this test was reaching for: the errno handling in the library itself is correct. I traced it. **DEFECT (medium) — this asserts on `errno` after intervening library calls that are allowed to change it.**
`AKSL_CHECK_STATUS` on line 65 does not just evaluate the expression. It calls `aksl_take()` (`tests/aksl_capture.h`), which runs **four `snprintf` calls and `akerr_release_error()`** before returning. Only then does line 66 read `errno`.
C permits a standard library function to set `errno` to a non-zero value even on a successful call. So this assertion is relying on glibc's `snprintf` happening not to touch `errno` for these particular inputs. It passes today. It is not guaranteed by anything, and it will fail on a libc where that is not true, with a failure message that points at `fstatat` rather than at `snprintf`.
Two further problems with it:
1. **It is redundant.** Line 65 already proved the wrapper reported `EINVAL`. Line 66 adds no coverage of the wrapper.
2. **It asserts a contract the library does not offer.** Nothing in `include/akstdlib.h` promises anything about `errno` after a wrapper returns — and it could not, because the NULL-argument paths (`src/stat.c:15-16`, `30-31`, `44`, `57-58`, `72-73`, `86`) return *before* `errno = 0` is ever reached, so on those paths `errno` is whatever stale value the caller already had. Codifying a post-call `errno` reading for one path out of two is how an accidental contract gets born.
**Fix:** delete line 66. The `errno = E2BIG` on line 64 is the valuable part — it proves the wrapper does not report a stale errno — and line 65 already checks it.
Worth stating the other half plainly, since it is the thing this test was reaching for: **the errno handling in the library itself is correct.** I traced it. `FAIL` expands to `ENSURE_ERROR_READY(e); e->status = __err;`, so `AKSL_ERRNO_OR` is evaluated *after* `akerr_next_error()` runs — and `akerr_next_error()` (`deps/libakerror/src/error.c:240-248`) is a bare loop over `AKERR_ARRAY_ERROR` with no library calls in it, so it cannot clobber `errno` in between. All six wrappers reset `errno` immediately before their libc call and read it before anything else can run. No defect there.
|
|||||||
AKSL_CHECK(symlink(path, linkpath) == 0);
|
AKSL_CHECK(symlink(path, linkpath) == 0);
|
||||||
@@ -57,13 +72,27 @@ static int test_stat_paths_and_fstatat(void)
|
|||||||
AKSL_CHECK(S_ISREG(dest.st_mode));
|
AKSL_CHECK(S_ISREG(dest.st_mode));
|
||||||
AKSL_CHECK(S_ISLNK(ldest.st_mode));
|
AKSL_CHECK(S_ISLNK(ldest.st_mode));
|
||||||
|
|
||||||
/* AT_FDCWD makes fstatat resolve this path from the current directory. */
|
/* A real dirfd must resolve a relative name against that directory. */
|
||||||
AKSL_CHECK_OK(aksl_fstatat(AT_FDCWD, path, &dest, 0));
|
AKSL_CHECK(snprintf(directory, sizeof(directory), "%s", path) < (int)sizeof(directory));
|
||||||
|
basename = strrchr(directory, '/');
|
||||||
|
AKSL_CHECK(basename != NULL);
|
||||||
|
*basename = '\0';
|
||||||
|
dirfd = open(directory, O_RDONLY | O_DIRECTORY);
|
||||||
|
AKSL_CHECK(dirfd >= 0);
|
||||||
|
AKSL_CHECK_OK(aksl_fstatat(dirfd, strrchr(path, '/') + 1, &dest, 0));
|
||||||
|
AKSL_CHECK(dest.st_ino == path_dest.st_ino);
|
||||||
|
AKSL_CHECK(close(dirfd) == 0);
|
||||||
|
|
||||||
|
/* A valid non-zero flag must reach libc, making this call an lstat. */
|
||||||
|
AKSL_CHECK_OK(aksl_fstatat(AT_FDCWD, linkpath, &dest, AT_SYMLINK_NOFOLLOW));
|
||||||
|
AKSL_CHECK(S_ISLNK(dest.st_mode));
|
||||||
|
|
||||||
/* Invalid flags must replace stale errno with the EINVAL libc reports. */
|
/* Invalid flags must replace stale errno with the EINVAL libc reports. */
|
||||||
errno = E2BIG;
|
errno = E2BIG;
|
||||||
AKSL_CHECK_STATUS(aksl_fstatat(AT_FDCWD, path, &dest, 0x40000000), EINVAL);
|
AKSL_CHECK_STATUS(aksl_fstatat(AT_FDCWD, path, &dest, AKSL_TEST_AT_INVALID), EINVAL);
|
||||||
AKSL_CHECK(errno == EINVAL);
|
|
||||||
|
/* statvfs reports ENOENT before it can describe a missing filesystem path. */
|
||||||
|
AKSL_CHECK_STATUS(aksl_statvfs("/nonexistent/aksl/stat", &vdest), ENOENT);
|
||||||
|
|
||||||
/* statvfs reports the filesystem containing the current directory. */
|
/* statvfs reports the filesystem containing the current directory. */
|
||||||
AKSL_CHECK_OK(aksl_statvfs(".", &vdest));
|
AKSL_CHECK_OK(aksl_statvfs(".", &vdest));
|
||||||
|
|||||||
DEFECT (high) — the header is not self-contained for
aksl_fstatat.Line 79-80 add
<sys/stat.h>and<sys/statvfs.h>, but not<fcntl.h>. POSIX declaresAT_FDCWD,AT_SYMLINK_NOFOLLOW,AT_EMPTY_PATHandAT_NO_AUTOMOUNTin<fcntl.h>, not in<sys/stat.h>. Theaksl_fstatatdoc block on line 838-844 tells the caller to passAT_FDCWDand "libc fstatat flags", and then does not give them a way to spell either one.Verified. A translation unit whose only include is
<akstdlib.h>:Adding
#include <fcntl.h>to the test file compiles it clean. The suite never caught this becausetests/test_stat.c:4includes<fcntl.h>itself, so the test is not testing the header the way a consumer sees it.Fix: add
#include <fcntl.h>here, beside the other two.