0.3.0: converge relibc to upstream 0.6.0 + Red Bear patches
This commit is contained in:
@@ -0,0 +1,252 @@
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
/* #if defined(__linux__) && !defined(__redox__) */
|
||||
/* #include <linux/version.h> */
|
||||
/* #else */
|
||||
/* #define KERNEL_VERSION(x,y,z) (0) */
|
||||
/* #endif */
|
||||
|
||||
__attribute__((nonnull(2)))
|
||||
static bool check_mode(
|
||||
int dir,
|
||||
const char path[],
|
||||
int flags,
|
||||
mode_t expected
|
||||
) {
|
||||
struct stat stat = {0};
|
||||
if (fstatat(dir, path, &stat, flags) == -1) {
|
||||
perror("fstatat");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((stat.st_mode & 0777) != expected) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"Mode mismatch\n\tExpected: %o\n\tActual: %o\n",
|
||||
expected,
|
||||
stat.st_mode & 0777
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
int status = EXIT_FAILURE;
|
||||
|
||||
char template[] = "/tmp/chmtest.XXXXXX";
|
||||
if (!mkdtemp(template)) {
|
||||
perror("mkdtemp");
|
||||
goto bye;
|
||||
}
|
||||
size_t len = sizeof(template) - 1;
|
||||
|
||||
// Create a file and a link to chmod.
|
||||
const char file_name[] = "king_crimson";
|
||||
char file_path[PATH_MAX] = {0};
|
||||
memcpy(file_path, template, len);
|
||||
file_path[len] = '/';
|
||||
memcpy(&file_path[len + 1], file_name, sizeof(file_name));
|
||||
|
||||
int filefd = open(file_path, O_CREAT);
|
||||
if (filefd == -1) {
|
||||
perror("open (file, O_CREAT)");
|
||||
goto rmtempdir;
|
||||
}
|
||||
|
||||
const char link_name[] = "red";
|
||||
char link_path[PATH_MAX] = {0};
|
||||
memcpy(link_path, template, len);
|
||||
link_path[len] = '/';
|
||||
memcpy(&link_path[len + 1], link_name, sizeof(link_name));
|
||||
|
||||
if (symlink(file_path, link_path) == -1) {
|
||||
perror("symlink");
|
||||
goto rmfiles;
|
||||
}
|
||||
#ifdef __redox__
|
||||
int linkfd = open(link_path, O_PATH | O_NOFOLLOW | O_SYMLINK);
|
||||
if (linkfd == -1) {
|
||||
perror("open (link, O_NOFOLLOW)");
|
||||
goto rmfiles;
|
||||
}
|
||||
#endif
|
||||
|
||||
int dir = open(template, O_DIRECTORY);
|
||||
if (dir == -1) {
|
||||
perror("open (temp dir)");
|
||||
goto closelink;
|
||||
}
|
||||
|
||||
// chmod
|
||||
|
||||
// File
|
||||
if (chmod(file_path, 0666) == -1) {
|
||||
perror("chmod file 0666");
|
||||
goto closetemp;
|
||||
}
|
||||
if (!check_mode(dir, file_name, 0, 0666)) {
|
||||
fprintf(stderr, "Context: chmod %s\n", file_name);
|
||||
goto closetemp;
|
||||
}
|
||||
|
||||
// Link (deferenced)
|
||||
if (chmod(link_path, 0777) == -1) {
|
||||
perror("chmod link 0777");
|
||||
goto closetemp;
|
||||
}
|
||||
if (!check_mode(dir, file_name, 0, 0777)) {
|
||||
fprintf(stderr, "Context: chmod %s\n", link_name);
|
||||
goto closetemp;
|
||||
}
|
||||
|
||||
// Dir
|
||||
if (chmod(template, 0766) == -1) {
|
||||
perror("chmod directory 0766");
|
||||
goto closetemp;
|
||||
}
|
||||
if (!check_mode(dir, "", AT_EMPTY_PATH, 0766)) {
|
||||
fprintf(stderr, "Context: chmod %s\n", template);
|
||||
goto closetemp;
|
||||
}
|
||||
|
||||
// fchmod
|
||||
|
||||
// File
|
||||
if (fchmod(filefd, 0644) == -1) {
|
||||
perror("fchmod file 0644");
|
||||
goto closetemp;
|
||||
}
|
||||
if (!check_mode(dir, file_name, 0, 0644)) {
|
||||
fprintf(stderr, "Context: fchmod %s\n", file_name);
|
||||
goto closetemp;
|
||||
}
|
||||
|
||||
// Link (not followed)
|
||||
#ifdef __redox__
|
||||
if (fchmod(linkfd, 0666) == -1) {
|
||||
perror("fchmod link 0666");
|
||||
goto closetemp;
|
||||
}
|
||||
if (!check_mode(dir, link_name, AT_SYMLINK_NOFOLLOW, 0666)) {
|
||||
fprintf(stderr, "Context: fchmod %s\n", link_name);
|
||||
goto closetemp;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Directory
|
||||
if (fchmod(dir, 0777) == -1) {
|
||||
perror("fchmod directory 0777");
|
||||
goto closetemp;
|
||||
}
|
||||
if (!check_mode(dir, "", AT_EMPTY_PATH, 0777)) {
|
||||
fprintf(stderr, "Context: fchmod %s\n", template);
|
||||
goto closetemp;
|
||||
}
|
||||
|
||||
// fchmodat
|
||||
|
||||
// File (relative)
|
||||
if (fchmodat(dir, file_name, 0666, 0) == -1) {
|
||||
perror("fchmodat directory 0666");
|
||||
goto closetemp;
|
||||
}
|
||||
if (!check_mode(dir, file_name, 0, 0666)) {
|
||||
fprintf(stderr, "Context: fchmodat %s\n", file_name);
|
||||
goto closetemp;
|
||||
}
|
||||
|
||||
// File (absolute)
|
||||
if (fchmodat(dir, file_path, 0777, 0) == -1) {
|
||||
perror("fchmodat file 0777");
|
||||
goto closetemp;
|
||||
}
|
||||
if (!check_mode(dir, file_name, 0, 0777)) {
|
||||
fprintf(stderr, "Context: fchmodat %s\n", file_path);
|
||||
}
|
||||
|
||||
// Link (followed)
|
||||
if (fchmodat(dir, link_name, 0666, 0) == -1) {
|
||||
perror("fchmodat link 0666");
|
||||
goto closetemp;
|
||||
}
|
||||
if (!check_mode(dir, file_name, 0, 0666)) {
|
||||
fprintf(stderr, "Context: fchmodat %s\n", link_name);
|
||||
goto closetemp;
|
||||
}
|
||||
|
||||
// Link (not followed)
|
||||
#ifdef __redox__
|
||||
if (fchmodat(dir, link_name, 0777, AT_SYMLINK_NOFOLLOW) == -1) {
|
||||
perror("fchmodat link 0777");
|
||||
goto closetemp;
|
||||
}
|
||||
if (!check_mode(dir, link_name, AT_SYMLINK_NOFOLLOW, 0777)) {
|
||||
fprintf(stderr, "Context: fchmodat %s\n", link_name);
|
||||
goto closetemp;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Directory
|
||||
// AT_EMPTY_PATH support is relatively new so this fails in CI.
|
||||
#if defined(__redox__) // || LINUX_VERSION_CODE >= KERNEL_VERSION(6,6,0)
|
||||
if (fchmodat(dir, "", 0700, AT_EMPTY_PATH) == -1) {
|
||||
perror("fchmodat directory 0700");
|
||||
goto closetemp;
|
||||
}
|
||||
if (!check_mode(dir, "", AT_EMPTY_PATH, 0700)) {
|
||||
fprintf(stderr, "Context: fchmodat %s\n", template);
|
||||
goto closetemp;
|
||||
}
|
||||
|
||||
// Directory (cwd)
|
||||
char old_cwd[PATH_MAX] = {0};
|
||||
if (!getcwd(old_cwd, PATH_MAX)) {
|
||||
perror("getcwd");
|
||||
goto closetemp;
|
||||
}
|
||||
if (chdir(template) == -1) {
|
||||
perror("chdir (temp dir)");
|
||||
goto closetemp;
|
||||
}
|
||||
|
||||
if (fchmodat(AT_FDCWD, "", 0777, AT_EMPTY_PATH) == -1) {
|
||||
perror("fchmodat cwd 0777");
|
||||
goto closetemp;
|
||||
}
|
||||
if (!check_mode(AT_FDCWD, "", AT_EMPTY_PATH, 0777)) {
|
||||
fprintf(stderr, "Context: fchmodat %s\n", template);
|
||||
goto closetemp;
|
||||
}
|
||||
|
||||
if (chdir(old_cwd) == -1) {
|
||||
perror("chdir (old cwd)");
|
||||
goto closetemp;
|
||||
}
|
||||
#endif
|
||||
|
||||
status = EXIT_SUCCESS;
|
||||
closetemp:
|
||||
close(dir);
|
||||
closelink:
|
||||
#ifdef __redox__
|
||||
close(linkfd);
|
||||
#endif
|
||||
rmfiles:
|
||||
close(filefd);
|
||||
unlink(file_path);
|
||||
unlink(link_path);
|
||||
rmtempdir:
|
||||
rmdir(template);
|
||||
bye:
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,356 @@
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
// Create file `dir/name` and fill it with `contents`.
|
||||
__attribute__((nonnull))
|
||||
static const char* mktestfile(
|
||||
const char dir[],
|
||||
const char name[],
|
||||
const char contents[]
|
||||
) {
|
||||
size_t dir_len = strlen(dir);
|
||||
size_t name_len = strlen(name);
|
||||
|
||||
// dir + / name + \0
|
||||
char* path = malloc(dir_len + name_len + 2);
|
||||
if (!path) {
|
||||
perror("malloc");
|
||||
return NULL;
|
||||
}
|
||||
// memcpy is faster/recommended but I'm lazy.
|
||||
strcpy(path, dir);
|
||||
strcat(path, "/");
|
||||
strcat(path, name);
|
||||
|
||||
int fd = creat(path, 0700);
|
||||
if (fd < 0) {
|
||||
perror("creat");
|
||||
free(path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t contents_len = strlen(contents);
|
||||
// Assumes that the handful of bytes of contents are fully written.
|
||||
if (write(fd, contents, contents_len) < 0) {
|
||||
perror("write");
|
||||
free(path);
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return path;
|
||||
}
|
||||
|
||||
// Create a symlink, `dir/name`, to `target` and return its path.
|
||||
__attribute__((nonnull))
|
||||
static const char* mktestlink(
|
||||
const char dir[],
|
||||
const char name[],
|
||||
const char target[]
|
||||
) {
|
||||
char* path = malloc(strlen(dir) + strlen(name) + 2);
|
||||
if (!path) {
|
||||
perror("malloc");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strcpy(path, dir);
|
||||
strcat(path, "/");
|
||||
strcat(path, name);
|
||||
|
||||
if (symlink(target, path) == -1) {
|
||||
perror("symlink");
|
||||
free(path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
__attribute__((nonnull))
|
||||
static int run_test(
|
||||
int fd,
|
||||
int flags,
|
||||
const char name[],
|
||||
const char path[],
|
||||
size_t expected,
|
||||
mode_t mode
|
||||
) {
|
||||
struct stat stat = {0};
|
||||
if (fstatat(fd, name, &stat, flags) == -1) {
|
||||
perror("fstatat");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((size_t) stat.st_size != expected) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"fstatat invalid stats\n\tFile: %s\n\tExpected: %zu Actual: %ld\n",
|
||||
path,
|
||||
expected,
|
||||
stat.st_size
|
||||
);
|
||||
return -1;
|
||||
}
|
||||
if ((stat.st_mode & S_IFMT) != mode) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"fstatat invalid mode\n\tFile: %s\n",
|
||||
path
|
||||
);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// We create the temp dirs/files therefore we should own them.
|
||||
uid_t uid = getuid();
|
||||
gid_t gid = getgid();
|
||||
if (stat.st_uid != uid || stat.st_gid != gid) {
|
||||
fputs("fstatat invalid UID or GID", stderr);
|
||||
fprintf(
|
||||
stderr,
|
||||
"\n\tExpected UID: %u\n\tActual UID: %u\n",
|
||||
uid, stat.st_uid
|
||||
);
|
||||
fprintf(
|
||||
stderr,
|
||||
"\n\tExpected GID: %u\n\tActual GID: %u\n",
|
||||
gid, stat.st_gid
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
int status = EXIT_FAILURE;
|
||||
|
||||
char dir_template_A[] = "/tmp/fsatest.XXXXXXX";
|
||||
char* dir_A = mkdtemp(dir_template_A);
|
||||
if (!dir_A) {
|
||||
perror("mkdtemp (dir A)");
|
||||
goto bye;
|
||||
}
|
||||
|
||||
char dir_template_B[] = "/tmp/fsatest.XXXXXXX";
|
||||
char* dir_B = mkdtemp(dir_template_B);
|
||||
if (!dir_B) {
|
||||
perror("mkdtemp (dir B)");
|
||||
goto clean_dir_a;
|
||||
}
|
||||
|
||||
// File in directory A
|
||||
const char name[] = "file";
|
||||
const char cont_A[] = "Flying Dutchman";
|
||||
const char* file_A = mktestfile(dir_A, name, cont_A);
|
||||
if (!file_A) {
|
||||
goto clean_dir_b;
|
||||
}
|
||||
// Link from directory A to file A
|
||||
const char link_A_name[] = "alink";
|
||||
const char* link_A = mktestlink(dir_A, link_A_name, file_A);
|
||||
if (!link_A) {
|
||||
goto clean_file_a;
|
||||
}
|
||||
|
||||
// File in directory B
|
||||
const char cont_B[] = "Every Villain is Lemons";
|
||||
const char* file_B = mktestfile(dir_B, name, cont_B);
|
||||
if (!file_B) {
|
||||
goto unlink_link_A;
|
||||
}
|
||||
const char link_B_name[] = "blink";
|
||||
// Link from directory A to file B in directory B
|
||||
const char* link_B = mktestlink(dir_A, link_B_name, file_B);
|
||||
if (!link_B) {
|
||||
goto unlink_file_B;
|
||||
}
|
||||
|
||||
// TESTS
|
||||
// The size of the file is used as a proxy for checking that stat worked.
|
||||
int dir_a_fd = open(dir_A, O_DIRECTORY);
|
||||
if (dir_a_fd == -1) {
|
||||
perror("open (dir A)");
|
||||
goto unlink_link_B;
|
||||
}
|
||||
|
||||
// fstatat works (basic)
|
||||
size_t len_cont_A = strlen(cont_A);
|
||||
if (run_test(dir_a_fd, 0, name, file_A, len_cont_A, S_IFREG) == -1) {
|
||||
goto close_dir_a_fd;
|
||||
}
|
||||
|
||||
// fstatat follows symlinks (same dir)
|
||||
if (run_test(dir_a_fd, 0, link_A_name, link_A, len_cont_A, S_IFREG) == -1) {
|
||||
fprintf(stderr, "Context: link %s -> %s\n", link_A, file_A);
|
||||
goto close_dir_a_fd;
|
||||
}
|
||||
|
||||
// fstatat follows symlinks (to diff dir)
|
||||
size_t len_cont_B = strlen(cont_B);
|
||||
if (run_test(dir_a_fd, 0, link_B_name, link_B, len_cont_B, S_IFREG) == -1) {
|
||||
fprintf(stderr, "Context: link %s -> %s\n", link_B, file_B);
|
||||
goto close_dir_a_fd;
|
||||
}
|
||||
|
||||
// AT_SYMLINK_NOFOLLOW
|
||||
if (
|
||||
run_test(
|
||||
dir_a_fd,
|
||||
AT_SYMLINK_NOFOLLOW,
|
||||
link_A_name,
|
||||
link_A,
|
||||
strlen(file_A),
|
||||
S_IFLNK
|
||||
) == -1
|
||||
) {
|
||||
fprintf(stderr, "Context: link %s (no follow)\n", link_A);
|
||||
goto close_dir_a_fd;
|
||||
}
|
||||
|
||||
// TODO: O_SEARCH (no Redox support)
|
||||
|
||||
// AT_FDCWD
|
||||
char old_cwd[PATH_MAX] = {0};
|
||||
if (!getcwd(old_cwd, PATH_MAX)) {
|
||||
perror("getcwd");
|
||||
goto close_dir_a_fd;
|
||||
}
|
||||
if (chdir(dir_A) == -1) {
|
||||
perror("chdir");
|
||||
goto close_dir_a_fd;
|
||||
}
|
||||
if (run_test(AT_FDCWD, 0, name, "./", len_cont_A, S_IFREG) == -1) {
|
||||
fputs("Context: AT_FDCWD\n", stderr);
|
||||
goto close_dir_a_fd;
|
||||
}
|
||||
if (chdir(old_cwd) == -1) {
|
||||
perror("chdir");
|
||||
goto close_dir_a_fd;
|
||||
}
|
||||
|
||||
// Absolute path
|
||||
if (run_test(dir_a_fd, 0, file_A, file_A, len_cont_A, S_IFREG) == -1) {
|
||||
fprintf(stderr, "Context: absolute path %s\n", file_A);
|
||||
goto close_dir_a_fd;
|
||||
}
|
||||
|
||||
// Relative path that traverses dir boundaries
|
||||
// The path isn't resolved beneath the directory but rather resolved
|
||||
// relative to it. Directory traversal is allowed if AT_RESOLVE_BENEATH
|
||||
// isn't used.
|
||||
const char nested_name[] = "nested";
|
||||
char* nested_dir = malloc(strlen(dir_A) + strlen(nested_name) + 2);
|
||||
if (!nested_dir) {
|
||||
perror("malloc");
|
||||
goto close_dir_a_fd;
|
||||
}
|
||||
strcpy(nested_dir, dir_A);
|
||||
strcat(nested_dir, "/");
|
||||
strcat(nested_dir, nested_name);
|
||||
if (mkdir(nested_dir, 0700) == -1) {
|
||||
perror("mkdir");
|
||||
goto clean_nested_dir;
|
||||
}
|
||||
|
||||
int nested_fd = open(nested_dir, O_DIRECTORY);
|
||||
if (nested_fd < 0) {
|
||||
perror("open");
|
||||
goto remove_nested_dir;
|
||||
}
|
||||
char rel_nested[sizeof(name) + 5] = "../";
|
||||
strcat(rel_nested, name);
|
||||
if (
|
||||
run_test(
|
||||
nested_fd,
|
||||
0,
|
||||
rel_nested,
|
||||
rel_nested,
|
||||
len_cont_A,
|
||||
S_IFREG
|
||||
) == -1
|
||||
) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"Context: relative path from %s to ../%s\n",
|
||||
nested_dir,
|
||||
name
|
||||
);
|
||||
goto close_nested_dir;
|
||||
}
|
||||
|
||||
// TODO: AT_RESOLVE_BENEATH
|
||||
|
||||
// AT_EMPTY_PATH (empty path)
|
||||
struct stat stat = {0};
|
||||
if (fstatat(dir_a_fd, "", &stat, AT_EMPTY_PATH) == -1) {
|
||||
fputs("Context: AT_EMPTY_PATH with empty path\n", stderr);
|
||||
goto close_dir_a_fd;
|
||||
}
|
||||
if ((stat.st_mode & S_IFMT) != S_IFDIR) {
|
||||
fputs("Context: AT_EMPTY_PATH should stat dir (empty path)\n", stderr);
|
||||
goto close_dir_a_fd;
|
||||
}
|
||||
|
||||
// AT_EMPTY_PATH (non-empty path)
|
||||
if (
|
||||
run_test(
|
||||
dir_a_fd,
|
||||
AT_EMPTY_PATH,
|
||||
link_A_name,
|
||||
link_A,
|
||||
len_cont_A,
|
||||
S_IFREG
|
||||
) == -1
|
||||
) {
|
||||
fputs("Context: AT_EMPTY_PATH with path should stat path\n", stderr);
|
||||
goto close_dir_a_fd;
|
||||
}
|
||||
|
||||
// TODO: Swapped directories resolves correctly
|
||||
|
||||
// Failure conditions:
|
||||
// Empty path without AT_EMPTY_PATH
|
||||
stat = (struct stat) {0};
|
||||
if (fstatat(dir_a_fd, "", &stat, 0) == 0) {
|
||||
fputs("Context: empty path should fail\n", stderr);
|
||||
goto close_dir_a_fd;
|
||||
}
|
||||
|
||||
// Clean up is LIFO where the last created resource is the first cleaned.
|
||||
status = EXIT_SUCCESS;
|
||||
close_nested_dir:
|
||||
close(nested_fd);
|
||||
remove_nested_dir:
|
||||
rmdir(nested_dir);
|
||||
clean_nested_dir:
|
||||
free((void*) nested_dir);
|
||||
close_dir_a_fd:
|
||||
close(dir_a_fd);
|
||||
unlink_link_B:
|
||||
unlink(link_B);
|
||||
free((void*) link_B);
|
||||
unlink_file_B:
|
||||
unlink(file_B);
|
||||
free((void*) file_B);
|
||||
unlink_link_A:
|
||||
unlink(link_A);
|
||||
free((void*) link_A);
|
||||
clean_file_a:
|
||||
unlink(file_A);
|
||||
free((void*) file_A);
|
||||
clean_dir_b:
|
||||
rmdir(dir_B);
|
||||
clean_dir_a:
|
||||
rmdir(dir_A);
|
||||
bye:
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
int main(void) {
|
||||
int status = EXIT_FAILURE;
|
||||
|
||||
char tempdir_path[] = "/tmp/lstat_test.XXXXXX";
|
||||
if (!mkdtemp(tempdir_path)) {
|
||||
perror("mkdtemp");
|
||||
goto bye;
|
||||
}
|
||||
int tempdir = open(tempdir_path, O_DIRECTORY);
|
||||
if (tempdir == -1) {
|
||||
perror("open (tempdir)");
|
||||
goto rmtemp;
|
||||
}
|
||||
|
||||
// Source
|
||||
const char src_name[] = "kerouac";
|
||||
char src_buf[PATH_MAX] = {0};
|
||||
strcpy(src_buf, tempdir_path);
|
||||
strcat(src_buf, "/");
|
||||
strcat(src_buf, src_name);
|
||||
// TODO: Use openat
|
||||
int source = open(src_buf, O_CREAT);
|
||||
if (source == -1) {
|
||||
perror("open");
|
||||
goto close_dir;
|
||||
}
|
||||
|
||||
// Dest (link)
|
||||
const char dst_name[] = "on_the_road";
|
||||
char dst_buf[PATH_MAX] = {0};
|
||||
strcpy(dst_buf, tempdir_path);
|
||||
strcat(dst_buf, "/");
|
||||
strcat(dst_buf, dst_name);
|
||||
if (symlink(src_buf, dst_buf) == -1) {
|
||||
perror("symlink");
|
||||
goto rm_source;
|
||||
}
|
||||
|
||||
struct stat src_stat = {0};
|
||||
if (fstatat(tempdir, dst_name, &src_stat, 0) == -1) {
|
||||
perror("fstatat");
|
||||
goto rm_dest;
|
||||
}
|
||||
// Quick consistency check
|
||||
uid_t uid = getuid();
|
||||
gid_t gid = getgid();
|
||||
if (src_stat.st_uid != uid) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"uid:\n\tExpected %ld\n\tActual: %ld\n",
|
||||
(intmax_t) uid,
|
||||
(intmax_t) gid
|
||||
);
|
||||
goto rm_dest;
|
||||
}
|
||||
if (src_stat.st_nlink != 1) {
|
||||
fputs("nlink: Expected one hard link\n", stderr);
|
||||
goto rm_dest;
|
||||
}
|
||||
if ((src_stat.st_mode & S_IFMT) != S_IFREG) {
|
||||
fputs("mode: Expected a normal file\n", stderr);
|
||||
goto rm_dest;
|
||||
}
|
||||
|
||||
struct stat dst_stat = {0};
|
||||
if (lstat(dst_buf, &dst_stat) == -1) {
|
||||
perror("lstat");
|
||||
goto rm_dest;
|
||||
}
|
||||
if (memcmp(&src_stat, &dst_stat, sizeof(struct stat)) == 0) {
|
||||
fputs("lstat incorrectly followed the symlink\n", stderr);
|
||||
goto rm_dest;
|
||||
}
|
||||
if ((dst_stat.st_mode & S_IFMT) != S_IFLNK) {
|
||||
fputs("lstat did not stat a link\n", stderr);
|
||||
goto rm_dest;
|
||||
}
|
||||
|
||||
status = EXIT_SUCCESS;
|
||||
rm_dest:
|
||||
unlink(dst_buf);
|
||||
rm_source:
|
||||
unlink(src_buf);
|
||||
close_dir:
|
||||
close(tempdir);
|
||||
rmtemp:
|
||||
rmdir(tempdir_path);
|
||||
bye:
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "test_helpers.h"
|
||||
|
||||
int main(void) {
|
||||
printf("sizeof(struct stat): %ld\n", sizeof(struct stat));
|
||||
|
||||
struct stat buf = {0};
|
||||
|
||||
int stat_status = stat("sys_stat/stat.c", &buf);
|
||||
ERROR_IF(stat, stat_status, == -1);
|
||||
UNEXP_IF(stat, stat_status, != 0);
|
||||
|
||||
printf("st_size: %lu\n", buf.st_size);
|
||||
printf("st_blksize: %lu\n", buf.st_blksize);
|
||||
printf("st_dev: %lu\n", buf.st_dev);
|
||||
printf("st_ino: %lu\n", buf.st_ino);
|
||||
printf("st_mode: %o\n", buf.st_mode);
|
||||
printf("st_nlink: %lu\n", buf.st_nlink);
|
||||
printf("st_uid: %u\n", buf.st_uid);
|
||||
printf("st_gid: %u\n", buf.st_gid);
|
||||
|
||||
assert(buf.st_size > 600);
|
||||
assert(buf.st_nlink == 1);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
Reference in New Issue
Block a user