mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-22 07:03:38 +00:00
Automated using this command: perl -pi.bak -e 's/(20[012][0-9])-20[12][01234]/$1-2025/g' `git ls-files`
701 lines
17 KiB
C
701 lines
17 KiB
C
/* Test FUSE.
|
|
* Copyright (C) 2009-2025 Red Hat Inc.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
/* This used to be a shell script test, but using C gives us finer
|
|
* control over exactly which system calls are being used, as well as
|
|
* allowing us to avoid one launch of the appliance during the test.
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <signal.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <errno.h>
|
|
#include <error.h>
|
|
|
|
#ifdef HAVE_ACL
|
|
#include <sys/acl.h>
|
|
#include <acl/libacl.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_SYS_XATTR_H
|
|
#include <sys/xattr.h>
|
|
#endif
|
|
|
|
#include <guestfs.h>
|
|
#include "guestfs-utils.h"
|
|
|
|
#include "ignore-value.h"
|
|
|
|
static guestfs_h *g;
|
|
|
|
#define SIZE INT64_C(1024*1024*1024)
|
|
|
|
/* NB: Must be a path that does not need quoting. */
|
|
static char mountpoint[] = "/tmp/testfuseXXXXXX";
|
|
|
|
static int acl_available;
|
|
static int linuxxattrs_available;
|
|
|
|
static void create_initial_filesystem (void);
|
|
static int test_fuse (void);
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
const char *s;
|
|
const char *acl_group[] = { "acl", NULL };
|
|
const char *linuxxattrs_group[] = { "linuxxattrs", NULL };
|
|
int debug_calls, r, res;
|
|
pid_t pid;
|
|
struct sigaction sa;
|
|
char cmd[128];
|
|
|
|
/* Allow the test to be skipped. Note I'm using the old shell
|
|
* script name here.
|
|
*/
|
|
s = getenv ("SKIP_TEST_FUSE_SH");
|
|
if (s && STRNEQ (s, "")) {
|
|
printf ("%s: test skipped because environment variable is set\n",
|
|
argv[0]);
|
|
exit (77);
|
|
}
|
|
|
|
if (access ("/dev/fuse", W_OK) == -1)
|
|
error (77, errno, "access: /dev/fuse");
|
|
|
|
g = guestfs_create ();
|
|
if (g == NULL)
|
|
error (EXIT_FAILURE, errno, "guestfs_create");
|
|
|
|
if (guestfs_add_drive_scratch (g, SIZE, -1) == -1)
|
|
exit (EXIT_FAILURE);
|
|
|
|
if (guestfs_launch (g) == -1)
|
|
exit (EXIT_FAILURE);
|
|
|
|
/* Test features. */
|
|
acl_available = guestfs_feature_available (g, (char **) acl_group);
|
|
if (acl_available == -1) exit (EXIT_FAILURE);
|
|
|
|
linuxxattrs_available =
|
|
guestfs_feature_available (g, (char **) linuxxattrs_group);
|
|
if (linuxxattrs_available == -1) exit (EXIT_FAILURE);
|
|
|
|
create_initial_filesystem ();
|
|
|
|
/* Make a mountpoint. */
|
|
if (mkdtemp (mountpoint) == NULL)
|
|
exit (EXIT_FAILURE);
|
|
|
|
/* Mount the filesystem on the host using FUSE. */
|
|
debug_calls = guestfs_get_trace (g);
|
|
if (guestfs_mount_local (g, mountpoint,
|
|
GUESTFS_MOUNT_LOCAL_DEBUGCALLS, debug_calls,
|
|
-1) == -1)
|
|
exit (EXIT_FAILURE);
|
|
|
|
/* Fork to run the next part of the test. */
|
|
pid = fork ();
|
|
if (pid == -1)
|
|
error (EXIT_FAILURE, errno, "fork");
|
|
|
|
if (pid == 0) { /* Child. */
|
|
/* Move into the mountpoint for the tests. */
|
|
if (chdir (mountpoint) == -1) {
|
|
perror (mountpoint);
|
|
_exit (EXIT_FAILURE);
|
|
}
|
|
|
|
res = test_fuse ();
|
|
printf ("test_fuse() returned %d\n", res);
|
|
fflush (stdout);
|
|
|
|
/* Move out of the mountpoint (otherwise our cwd will prevent the
|
|
* mountpoint from being unmounted below).
|
|
*/
|
|
ignore_value (chdir ("/"));
|
|
|
|
/* Who's using the mountpoint? Should be no one. */
|
|
snprintf (cmd, sizeof cmd, "%s %s", FUSER, mountpoint);
|
|
printf ("%s\n", cmd);
|
|
fflush (stdout);
|
|
ignore_value (system (cmd));
|
|
|
|
/* Unmount it. */
|
|
snprintf (cmd, sizeof cmd, "guestunmount %s", mountpoint);
|
|
printf ("%s\n", cmd);
|
|
fflush (stdout);
|
|
r = system (cmd);
|
|
if (!WIFEXITED (r) || WEXITSTATUS (r) != EXIT_SUCCESS)
|
|
fprintf (stderr, "%s: warning: guestunmount command failed\n", argv[0]);
|
|
|
|
_exit (res == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
|
|
}
|
|
|
|
/* Parent. */
|
|
|
|
/* Ignore signals in the parent while running the child. */
|
|
memset (&sa, 0, sizeof sa);
|
|
sa.sa_handler = SIG_IGN;
|
|
sigaction (SIGINT, &sa, NULL);
|
|
sigaction (SIGTERM, &sa, NULL);
|
|
|
|
if (guestfs_mount_local_run (g) == -1)
|
|
exit (EXIT_FAILURE);
|
|
|
|
/* Clean up and exit. */
|
|
if (waitpid (pid, &r, 0) == -1)
|
|
error (EXIT_FAILURE, errno, "waitpid");
|
|
|
|
if (rmdir (mountpoint) == -1)
|
|
error (EXIT_FAILURE, errno, "rmdir: %s", mountpoint);
|
|
|
|
if (guestfs_shutdown (g) == -1)
|
|
exit (EXIT_FAILURE);
|
|
|
|
guestfs_close (g);
|
|
|
|
/* Did the child process fail? */
|
|
exit (!WIFEXITED (r) || WEXITSTATUS (r) != 0 ? EXIT_FAILURE : EXIT_SUCCESS);
|
|
}
|
|
|
|
/* Create a filesystem with some initial content. */
|
|
static void
|
|
create_initial_filesystem (void)
|
|
{
|
|
if (guestfs_part_disk (g, "/dev/sda", "mbr") == -1)
|
|
exit (EXIT_FAILURE);
|
|
|
|
/* Use ext4 because it supports modern features. Use >= 256 byte
|
|
* inodes because these support nanosecond timestamps.
|
|
*/
|
|
if (guestfs_mkfs_opts (g, "ext4", "/dev/sda1",
|
|
GUESTFS_MKFS_OPTS_INODE, 256,
|
|
-1) == -1)
|
|
exit (EXIT_FAILURE);
|
|
|
|
if (guestfs_mount_options (g, "acl,user_xattr", "/dev/sda1", "/") == -1)
|
|
exit (EXIT_FAILURE);
|
|
|
|
if (guestfs_write (g, "/hello.txt", "hello", 5) == -1)
|
|
exit (EXIT_FAILURE);
|
|
|
|
if (guestfs_write (g, "/world.txt", "hello world", 11) == -1)
|
|
exit (EXIT_FAILURE);
|
|
|
|
if (guestfs_touch (g, "/empty") == -1)
|
|
exit (EXIT_FAILURE);
|
|
|
|
if (linuxxattrs_available) {
|
|
if (guestfs_touch (g, "/user_xattr") == -1)
|
|
exit (EXIT_FAILURE);
|
|
|
|
if (guestfs_setxattr (g, "user.test", "hello123", 8, "/user_xattr") == -1)
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
|
|
if (acl_available) {
|
|
if (guestfs_touch (g, "/acl") == -1)
|
|
exit (EXIT_FAILURE);
|
|
|
|
if (guestfs_acl_set_file (g, "/acl", "access",
|
|
"u::rwx,u:500:r,g::rwx,m::rwx,o::r-x") == -1)
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
/* Run FUSE tests. Mountpoint is current directory. */
|
|
static int
|
|
test_fuse (void)
|
|
{
|
|
int stage = 0;
|
|
#define STAGE(fs,...) \
|
|
printf ("%02d: " fs "\n", ++stage, ##__VA_ARGS__); \
|
|
fflush (stdout)
|
|
FILE *fp;
|
|
char *line = NULL;
|
|
size_t len = 0;
|
|
struct stat statbuf;
|
|
char buf[128];
|
|
ssize_t r;
|
|
unsigned u, u1;
|
|
int fd;
|
|
struct timeval tv[2];
|
|
struct timespec ts[2];
|
|
#ifdef HAVE_ACL
|
|
acl_t acl;
|
|
char *acl_text;
|
|
#endif
|
|
|
|
STAGE ("checking initial files exist");
|
|
|
|
if (access ("empty", F_OK) == -1) {
|
|
perror ("access: empty");
|
|
return -1;
|
|
}
|
|
if (access ("hello.txt", F_OK) == -1) {
|
|
perror ("access: hello.txt");
|
|
return -1;
|
|
}
|
|
if (access ("world.txt", F_OK) == -1) {
|
|
perror ("access: world.txt");
|
|
return -1;
|
|
}
|
|
|
|
STAGE ("checking initial files contain expected content");
|
|
|
|
fp = fopen ("hello.txt", "r");
|
|
if (fp == NULL) {
|
|
perror ("open: hello.txt");
|
|
return -1;
|
|
}
|
|
if (getline (&line, &len, fp) == -1) {
|
|
perror ("getline: hello.txt");
|
|
fclose (fp);
|
|
return -1;
|
|
}
|
|
if (STRNEQ (line, "hello")) {
|
|
fprintf (stderr, "'hello.txt' does not contain expected content\n");
|
|
fclose (fp);
|
|
return -1;
|
|
}
|
|
fclose (fp);
|
|
|
|
fp = fopen ("world.txt", "r");
|
|
if (fp == NULL) {
|
|
perror ("open: world.txt");
|
|
return -1;
|
|
}
|
|
if (getline (&line, &len, fp) == -1) {
|
|
perror ("getline: world.txt");
|
|
fclose (fp);
|
|
return -1;
|
|
}
|
|
if (STRNEQ (line, "hello world")) {
|
|
fprintf (stderr, "'world.txt' does not contain expected content\n");
|
|
fclose (fp);
|
|
return -1;
|
|
}
|
|
fclose (fp);
|
|
|
|
STAGE ("checking file modes and sizes of initial content");
|
|
|
|
if (stat ("empty", &statbuf) == -1) {
|
|
perror ("stat: empty");
|
|
return -1;
|
|
}
|
|
if ((statbuf.st_mode & 0777) != 0644) {
|
|
fprintf (stderr, "'empty' has invalid mode (%o)\n", statbuf.st_mode);
|
|
return -1;
|
|
}
|
|
if (statbuf.st_size != 0) {
|
|
fprintf (stderr, "'empty' has invalid size (%d)\n", (int) statbuf.st_size);
|
|
return -1;
|
|
}
|
|
|
|
if (stat ("hello.txt", &statbuf) == -1) {
|
|
perror ("stat: hello.txt");
|
|
return -1;
|
|
}
|
|
if ((statbuf.st_mode & 0777) != 0644) {
|
|
fprintf (stderr, "'hello.txt' has invalid mode (%o)\n", statbuf.st_mode);
|
|
return -1;
|
|
}
|
|
if (statbuf.st_size != 5) {
|
|
fprintf (stderr, "'hello.txt' has invalid size (%d)\n",
|
|
(int) statbuf.st_size);
|
|
return -1;
|
|
}
|
|
|
|
if (stat ("world.txt", &statbuf) == -1) {
|
|
perror ("stat: world.txt");
|
|
return -1;
|
|
}
|
|
if ((statbuf.st_mode & 0777) != 0644) {
|
|
fprintf (stderr, "'world.txt' has invalid mode (%o)\n", statbuf.st_mode);
|
|
return -1;
|
|
}
|
|
if (statbuf.st_size != 11) {
|
|
fprintf (stderr, "'world.txt' has invalid size (%d)\n",
|
|
(int) statbuf.st_size);
|
|
return -1;
|
|
}
|
|
|
|
STAGE ("checking unlink");
|
|
|
|
fp = fopen ("new", "w");
|
|
if (fp == NULL) {
|
|
perror ("open: new");
|
|
return -1;
|
|
}
|
|
fclose (fp);
|
|
|
|
if (unlink ("new") == -1) {
|
|
perror ("unlink: new");
|
|
return -1;
|
|
}
|
|
|
|
STAGE ("checking symbolic link");
|
|
|
|
if (symlink ("hello.txt", "symlink") == -1) {
|
|
perror ("symlink: hello.txt, symlink");
|
|
return -1;
|
|
}
|
|
if (lstat ("symlink", &statbuf) == -1) {
|
|
perror ("lstat: symlink");
|
|
return -1;
|
|
}
|
|
if (!S_ISLNK (statbuf.st_mode)) {
|
|
fprintf (stderr, "'symlink' is not a symlink (mode = %o)\n",
|
|
statbuf.st_mode);
|
|
return -1;
|
|
}
|
|
|
|
STAGE ("checking readlink");
|
|
|
|
r = readlink ("symlink", buf, sizeof buf);
|
|
if (r == -1) {
|
|
perror ("readlink: symlink");
|
|
return -1;
|
|
}
|
|
/* readlink return buffer is not \0-terminated, hence: */
|
|
if (r != 9 || memcmp (buf, "hello.txt", r) != 0) {
|
|
fprintf (stderr, "readlink on 'symlink' returned incorrect result\n");
|
|
return -1;
|
|
}
|
|
|
|
STAGE ("checking hard link");
|
|
|
|
if (stat ("hello.txt", &statbuf) == -1) {
|
|
perror ("stat: hello.txt");
|
|
return -1;
|
|
}
|
|
if (statbuf.st_nlink != 1) {
|
|
fprintf (stderr, "nlink of 'hello.txt' was %d (expected 1)\n",
|
|
(int) statbuf.st_nlink);
|
|
return -1;
|
|
}
|
|
|
|
if (link ("hello.txt", "link") == -1) {
|
|
perror ("link: hello.txt, link");
|
|
return -1;
|
|
}
|
|
|
|
if (stat ("link", &statbuf) == -1) {
|
|
perror ("stat: link");
|
|
return -1;
|
|
}
|
|
if (statbuf.st_nlink != 2) {
|
|
fprintf (stderr, "nlink of 'link' was %d (expected 2)\n",
|
|
(int) statbuf.st_nlink);
|
|
return -1;
|
|
}
|
|
|
|
#if 0
|
|
/* This fails because of caching. The problem is that the linked file
|
|
* ("hello.txt") is cached with a link count of 1. unlink("link")
|
|
* invalidates the cache for "link", but _not_ for "hello.txt" which
|
|
* still has the now-incorrect cached value. However there's not much
|
|
* we can do about this since searching for all linked inodes of a file
|
|
* is an O(n) operation.
|
|
*/
|
|
if (stat ("hello.txt", &statbuf) == -1) {
|
|
perror ("stat: hello.txt");
|
|
return -1;
|
|
}
|
|
if (statbuf.st_nlink != 2) {
|
|
fprintf (stderr, "nlink of 'hello.txt' was %d (expected 2)\n",
|
|
(int) statbuf.st_nlink);
|
|
return -1;
|
|
}
|
|
|
|
if (unlink ("link") == -1) {
|
|
perror ("unlink: link");
|
|
return -1;
|
|
}
|
|
|
|
if (stat ("hello.txt", &statbuf) == -1) {
|
|
perror ("stat: hello.txt");
|
|
return -1;
|
|
}
|
|
if (statbuf.st_nlink != 1) {
|
|
fprintf (stderr, "nlink of 'hello.txt' was %d (expected 1)\n",
|
|
(int) statbuf.st_nlink);
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
STAGE ("checking mkdir");
|
|
|
|
if (mkdir ("newdir", 0755) == -1) {
|
|
perror ("mkdir: newdir");
|
|
return -1;
|
|
}
|
|
|
|
STAGE ("checking rmdir");
|
|
|
|
if (rmdir ("newdir") == -1) {
|
|
perror ("rmdir: newdir");
|
|
return -1;
|
|
}
|
|
|
|
STAGE ("checking rename");
|
|
|
|
fp = fopen ("old", "w");
|
|
if (fp == NULL) {
|
|
perror ("open: old");
|
|
return -1;
|
|
}
|
|
fclose (fp);
|
|
if (rename ("old", "new") == -1) {
|
|
perror ("rename: old, new");
|
|
return -1;
|
|
}
|
|
if (access ("new", F_OK) == -1) {
|
|
perror ("access: new");
|
|
return -1;
|
|
}
|
|
if (access ("old", F_OK) == 0) {
|
|
fprintf (stderr, "file 'old' exists after rename\n");
|
|
return -1;
|
|
}
|
|
if (unlink ("new") == -1) {
|
|
perror ("unlink: new");
|
|
return -1;
|
|
}
|
|
|
|
STAGE ("checking chmod");
|
|
|
|
fp = fopen ("new", "w");
|
|
if (fp == NULL) {
|
|
perror ("open: new");
|
|
return -1;
|
|
}
|
|
fclose (fp);
|
|
for (u = 0; u < 0777; u += 0111) {
|
|
if (chmod ("new", u) == -1) {
|
|
perror ("chmod: new");
|
|
return -1;
|
|
}
|
|
if (stat ("new", &statbuf) == -1) {
|
|
perror ("stat: new");
|
|
return -1;
|
|
}
|
|
if ((statbuf.st_mode & 0777) != u) {
|
|
fprintf (stderr, "unexpected mode: was %o expected %o\n",
|
|
statbuf.st_mode, u);
|
|
return -1;
|
|
}
|
|
}
|
|
if (unlink ("new") == -1) {
|
|
perror ("unlink: new");
|
|
return -1;
|
|
}
|
|
|
|
STAGE ("checking truncate");
|
|
|
|
fp = fopen ("truncated", "w");
|
|
if (fp == NULL) {
|
|
perror ("open: truncated");
|
|
return -1;
|
|
}
|
|
fclose (fp);
|
|
for (u = 10000; u <= 10000; u -= 1000) {
|
|
if (truncate ("truncated", u) == -1) {
|
|
perror ("truncate");
|
|
return -1;
|
|
}
|
|
if (stat ("truncated", &statbuf) == -1) {
|
|
perror ("stat: truncated");
|
|
return -1;
|
|
}
|
|
if (statbuf.st_size != u) {
|
|
fprintf (stderr, "unexpected size: was %u expected %u\n",
|
|
(unsigned) statbuf.st_size, u);
|
|
return -1;
|
|
}
|
|
}
|
|
if (unlink ("truncated") == -1) {
|
|
perror ("unlink: truncated");
|
|
return -1;
|
|
}
|
|
|
|
STAGE ("checking utimes");
|
|
|
|
fd = open ("timestamp", O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY|O_CLOEXEC, 0644);
|
|
if (fd == -1) {
|
|
perror ("open: timestamp");
|
|
return -1;
|
|
}
|
|
close (fd);
|
|
tv[0].tv_sec = 23; /* atime */
|
|
tv[0].tv_usec = 45;
|
|
tv[1].tv_sec = 67; /* mtime */
|
|
tv[1].tv_usec = 89;
|
|
if (utimes ("timestamp", tv) == -1) {
|
|
perror ("utimes: timestamp");
|
|
return -1;
|
|
}
|
|
if (stat ("timestamp", &statbuf) == -1) {
|
|
perror ("fstat: timestamp");
|
|
return -1;
|
|
}
|
|
if (statbuf.st_atime != 23 ||
|
|
statbuf.st_atim.tv_nsec != 45000 ||
|
|
statbuf.st_mtime != 67 ||
|
|
statbuf.st_mtim.tv_nsec != 89000) {
|
|
fprintf (stderr, "utimes did not set time (%d/%d/%d/%d)\n",
|
|
(int) statbuf.st_atime, (int) statbuf.st_atim.tv_nsec,
|
|
(int) statbuf.st_mtime, (int) statbuf.st_mtim.tv_nsec);
|
|
return -1;
|
|
}
|
|
|
|
STAGE ("checking utimens");
|
|
|
|
fd = open ("timestamp", O_WRONLY|O_CREAT|O_TRUNC|O_NOCTTY|O_CLOEXEC, 0644);
|
|
if (fd == -1) {
|
|
perror ("open: timestamp");
|
|
return -1;
|
|
}
|
|
ts[0].tv_sec = 12; /* atime */
|
|
ts[0].tv_nsec = 34;
|
|
ts[1].tv_sec = 56; /* mtime */
|
|
ts[1].tv_nsec = 78;
|
|
if (futimens (fd, ts) == -1) {
|
|
perror ("futimens: timestamp");
|
|
close (fd);
|
|
return -1;
|
|
}
|
|
if (fstat (fd, &statbuf) == -1) {
|
|
perror ("fstat: timestamp");
|
|
close (fd);
|
|
return -1;
|
|
}
|
|
if (statbuf.st_atime != 12 ||
|
|
statbuf.st_atim.tv_nsec != 34 ||
|
|
statbuf.st_mtime != 56 ||
|
|
statbuf.st_mtim.tv_nsec != 78) {
|
|
fprintf (stderr, "utimens did not set time (%d/%d/%d/%d)\n",
|
|
(int) statbuf.st_atime, (int) statbuf.st_atim.tv_nsec,
|
|
(int) statbuf.st_mtime, (int) statbuf.st_mtim.tv_nsec);
|
|
close (fd);
|
|
return -1;
|
|
}
|
|
close (fd);
|
|
|
|
STAGE ("checking writes");
|
|
|
|
fp = fopen ("new.txt", "w");
|
|
if (fp == NULL) {
|
|
perror ("open: new.txt");
|
|
return -1;
|
|
}
|
|
for (u = 0; u < 1000; ++u) {
|
|
if (fprintf (fp, "line %u\n", u) == -1) {
|
|
perror ("fprintf: new.txt");
|
|
fclose (fp);
|
|
return -1;
|
|
}
|
|
}
|
|
if (fclose (fp) == -1) {
|
|
perror ("fclose: new.txt");
|
|
return -1;
|
|
}
|
|
|
|
fp = fopen ("new.txt", "r");
|
|
if (fp == NULL) {
|
|
perror ("open: new.txt");
|
|
return -1;
|
|
}
|
|
for (u = 0; u < 1000; ++u) {
|
|
if (getline (&line, &len, fp) == -1) {
|
|
perror ("getline: new.txt");
|
|
fclose (fp);
|
|
return -1;
|
|
}
|
|
if (sscanf (line, "line %u", &u1) != 1 || u != u1) {
|
|
fprintf (stderr, "unexpected content: %s\n", line);
|
|
fclose (fp);
|
|
return -1;
|
|
}
|
|
}
|
|
fclose (fp);
|
|
|
|
#ifdef HAVE_ACL
|
|
if (acl_available) {
|
|
STAGE ("checking POSIX ACL read operation");
|
|
|
|
acl = acl_get_file ("acl", ACL_TYPE_ACCESS);
|
|
if (acl == (acl_t) NULL) {
|
|
perror ("acl_get_file: acl");
|
|
return -1;
|
|
}
|
|
acl_text = acl_to_any_text (acl, NULL, '\n', TEXT_SOME_EFFECTIVE | TEXT_NUMERIC_IDS);
|
|
if (acl_text == NULL) {
|
|
perror ("acl_to_any_text: acl");
|
|
return -1;
|
|
}
|
|
if (STRNEQ (acl_text, "user::rwx\nuser:500:r--\ngroup::rwx\nmask::rwx\nother::r-x")) {
|
|
fprintf (stderr, "unexpected acl: %s\n", acl_text);
|
|
return -1;
|
|
}
|
|
acl_free (acl_text);
|
|
acl_free (acl);
|
|
}
|
|
#endif
|
|
|
|
#if HAVE_GETXATTR
|
|
if (linuxxattrs_available) {
|
|
STAGE ("checking extended attribute (xattr) read operation");
|
|
|
|
r = getxattr ("user_xattr", "user.test", buf, sizeof buf);
|
|
if (r == -1) {
|
|
perror ("getxattr");
|
|
return -1;
|
|
}
|
|
if (r != 8 || memcmp (buf, "hello123", r) != 0) {
|
|
fprintf (stderr, "user.test xattr on file user_xattr was incorrect\n");
|
|
return -1;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* XXX:
|
|
These ones are not yet tested by the current program:
|
|
- statfs/statvfs
|
|
|
|
These ones cannot easily be tested by the current program, because
|
|
this program doesn't run as root:
|
|
- fsync
|
|
- chown
|
|
- mknod
|
|
*/
|
|
|
|
free (line);
|
|
return 0;
|
|
}
|