mirror of
https://sourceware.org/git/glibc.git
synced 2024-11-26 23:10:06 +00:00
198 lines
6.2 KiB
C
198 lines
6.2 KiB
C
|
/* FUSE-based test for mkstemp.
|
||
|
Copyright (C) 2024 Free Software Foundation, Inc.
|
||
|
This file is part of the GNU C Library.
|
||
|
|
||
|
The GNU C Library is free software; you can redistribute it and/or
|
||
|
modify it under the terms of the GNU Lesser General Public
|
||
|
License as published by the Free Software Foundation; either
|
||
|
version 2.1 of the License, or (at your option) any later version.
|
||
|
|
||
|
The GNU C Library 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
|
||
|
Lesser General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU Lesser General Public
|
||
|
License along with the GNU C Library; if not, see
|
||
|
<https://www.gnu.org/licenses/>. */
|
||
|
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
#include <errno.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <stdint.h>
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <support/check.h>
|
||
|
#include <support/fuse.h>
|
||
|
#include <support/support.h>
|
||
|
#include <support/xunistd.h>
|
||
|
|
||
|
/* Set to true in do_test to cause the first FUSE_CREATE attempt to fail. */
|
||
|
static _Atomic bool simulate_creat_race;
|
||
|
|
||
|
/* Basic tests with eventually successful creation. */
|
||
|
static void
|
||
|
fuse_thread_basic (struct support_fuse *f, void *closure)
|
||
|
{
|
||
|
char *previous_name = NULL;
|
||
|
int state = 0;
|
||
|
struct fuse_in_header *inh;
|
||
|
while ((inh = support_fuse_next (f)) != NULL)
|
||
|
{
|
||
|
if (support_fuse_handle_mountpoint (f)
|
||
|
|| (inh->nodeid == 1 && support_fuse_handle_directory (f)))
|
||
|
continue;
|
||
|
|
||
|
switch (inh->opcode)
|
||
|
{
|
||
|
case FUSE_LOOKUP:
|
||
|
/* File does not exist initially. */
|
||
|
TEST_COMPARE (inh->nodeid, 1);
|
||
|
if (simulate_creat_race)
|
||
|
{
|
||
|
if (state < 3)
|
||
|
++state;
|
||
|
else
|
||
|
FAIL ("invalid state: %d", state);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TEST_COMPARE (state, 0);
|
||
|
state = 3;
|
||
|
}
|
||
|
support_fuse_reply_error (f, ENOENT);
|
||
|
break;
|
||
|
case FUSE_CREATE:
|
||
|
{
|
||
|
TEST_COMPARE (inh->nodeid, 1);
|
||
|
char *name;
|
||
|
struct fuse_create_in *p
|
||
|
= support_fuse_cast_name (CREATE, inh, &name);
|
||
|
/* Name follows after struct fuse_create_in. */
|
||
|
TEST_COMPARE (p->flags & O_ACCMODE, O_RDWR);
|
||
|
TEST_VERIFY (p->flags & O_EXCL);
|
||
|
TEST_VERIFY (p->flags & O_CREAT);
|
||
|
TEST_COMPARE (p->mode & 07777, 0600);
|
||
|
TEST_VERIFY (S_ISREG (p->mode));
|
||
|
TEST_COMPARE_BLOB (name, 3, "new", 3);
|
||
|
|
||
|
if (state != 3 && simulate_creat_race)
|
||
|
{
|
||
|
++state;
|
||
|
support_fuse_reply_error (f, EEXIST);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (previous_name != NULL)
|
||
|
/* This test has a very small probability of failure
|
||
|
due to a harmless collision (one in 62**6 tests). */
|
||
|
TEST_VERIFY (strcmp (name, previous_name) != 0);
|
||
|
TEST_COMPARE (state, 3);
|
||
|
++state;
|
||
|
struct fuse_entry_out *entry;
|
||
|
struct fuse_open_out *open;
|
||
|
support_fuse_prepare_create (f, 2, &entry, &open);
|
||
|
entry->attr.mode = S_IFREG | 0600;
|
||
|
support_fuse_reply_prepared (f);
|
||
|
}
|
||
|
free (previous_name);
|
||
|
previous_name = xstrdup (name);
|
||
|
}
|
||
|
break;
|
||
|
case FUSE_FLUSH:
|
||
|
case FUSE_RELEASE:
|
||
|
TEST_COMPARE (state, 4);
|
||
|
TEST_COMPARE (inh->nodeid, 2);
|
||
|
support_fuse_reply_empty (f);
|
||
|
break;
|
||
|
default:
|
||
|
support_fuse_reply_error (f, EIO);
|
||
|
}
|
||
|
}
|
||
|
free (previous_name);
|
||
|
}
|
||
|
|
||
|
/* Reply that all files exist. */
|
||
|
static void
|
||
|
fuse_thread_eexist (struct support_fuse *f, void *closure)
|
||
|
{
|
||
|
uint64_t counter = 0;
|
||
|
struct fuse_in_header *inh;
|
||
|
while ((inh = support_fuse_next (f)) != NULL)
|
||
|
{
|
||
|
if (support_fuse_handle_mountpoint (f)
|
||
|
|| (inh->nodeid == 1 && support_fuse_handle_directory (f)))
|
||
|
continue;
|
||
|
|
||
|
switch (inh->opcode)
|
||
|
{
|
||
|
case FUSE_LOOKUP:
|
||
|
++counter;
|
||
|
TEST_COMPARE (inh->nodeid, 1);
|
||
|
char *name = support_fuse_cast (LOOKUP, inh);
|
||
|
TEST_COMPARE_BLOB (name, 3, "new", 3);
|
||
|
TEST_COMPARE (strlen (name), 9);
|
||
|
for (int i = 3; i <= 8; ++i)
|
||
|
{
|
||
|
/* The glibc implementation uses letters and digits only. */
|
||
|
char ch = name[i];
|
||
|
TEST_VERIFY (('0' <= ch && ch <= '9')
|
||
|
|| ('a' <= ch && ch <= 'z')
|
||
|
|| ('A' <= ch && ch <= 'Z'));
|
||
|
}
|
||
|
struct fuse_entry_out out =
|
||
|
{
|
||
|
.nodeid = 2,
|
||
|
.attr = {
|
||
|
.mode = S_IFREG | 0600,
|
||
|
.ino = 2,
|
||
|
},
|
||
|
};
|
||
|
support_fuse_reply (f, &out, sizeof (out));
|
||
|
break;
|
||
|
default:
|
||
|
support_fuse_reply_error (f, EIO);
|
||
|
}
|
||
|
}
|
||
|
/* Verify that mkstemp has retried a lot. The current
|
||
|
implementation tries 62 * 62 * 62 times until it goves up. */
|
||
|
TEST_VERIFY (counter >= 200000);
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
do_test (void)
|
||
|
{
|
||
|
support_fuse_init ();
|
||
|
|
||
|
for (int do_simulate_creat_race = 0; do_simulate_creat_race < 2;
|
||
|
++do_simulate_creat_race)
|
||
|
{
|
||
|
simulate_creat_race = do_simulate_creat_race;
|
||
|
printf ("info: testing with simulate_creat_race == %d\n",
|
||
|
(int) simulate_creat_race);
|
||
|
struct support_fuse *f = support_fuse_mount (fuse_thread_basic, NULL);
|
||
|
char *path = xasprintf ("%s/newXXXXXX", support_fuse_mountpoint (f));
|
||
|
int fd = mkstemp (path);
|
||
|
TEST_VERIFY (fd > 2);
|
||
|
xclose (fd);
|
||
|
free (path);
|
||
|
support_fuse_unmount (f);
|
||
|
}
|
||
|
|
||
|
puts ("info: testing EEXIST failure case for mkstemp");
|
||
|
{
|
||
|
struct support_fuse *f = support_fuse_mount (fuse_thread_eexist, NULL);
|
||
|
char *path = xasprintf ("%s/newXXXXXX", support_fuse_mountpoint (f));
|
||
|
errno = 0;
|
||
|
TEST_COMPARE (mkstemp (path), -1);
|
||
|
TEST_COMPARE (errno, EEXIST);
|
||
|
free (path);
|
||
|
support_fuse_unmount (f);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#include <support/test-driver.c>
|