#include <unistd.h>
struct _p11_save_file {
- char *path;
+ char *bare;
+ char *extension;
char *temp;
int fd;
int flags;
int flags;
};
+static char * make_unique_name (const char *bare,
+ const char *extension,
+ int (*check) (void *, char *),
+ void *data);
+
bool
p11_save_write_and_finish (p11_save_file *file,
const void *data,
return false;
ret = p11_save_write (file, data, length);
- if (!p11_save_finish_file (file, ret))
+ if (!p11_save_finish_file (file, NULL, ret))
ret = false;
return ret;
p11_save_file *
p11_save_open_file (const char *path,
+ const char *extension,
int flags)
{
- struct stat st;
p11_save_file *file;
char *temp;
int fd;
return_val_if_fail (path != NULL, NULL);
- /*
- * This is just an early convenience check. We check again
- * later when committing, in a non-racy fashion.
- */
+ if (extension == NULL)
+ extension = "";
- if (!(flags & P11_SAVE_OVERWRITE)) {
- if (stat (path, &st) >= 0) {
- p11_message ("file already exists: %s", path);
- return NULL;
- }
- }
-
- if (asprintf (&temp, "%s.XXXXXX", path) < 0)
+ if (asprintf (&temp, "%s%s.XXXXXX", path, extension) < 0)
return_val_if_reached (NULL);
fd = mkstemp (temp);
if (fd < 0) {
- p11_message ("couldn't create file: %s: %s",
- path, strerror (errno));
+ p11_message ("couldn't create file: %s%s: %s",
+ path, extension, strerror (errno));
free (temp);
return NULL;
}
file = calloc (1, sizeof (p11_save_file));
return_val_if_fail (file != NULL, NULL);
file->temp = temp;
- file->path = strdup (path);
- return_val_if_fail (file->path != NULL, NULL);
+ file->bare = strdup (path);
+ return_val_if_fail (file->bare != NULL, NULL);
+ file->extension = strdup (extension);
+ return_val_if_fail (file->extension != NULL, NULL);
file->flags = flags;
file->fd = fd;
filo_free (p11_save_file *file)
{
free (file->temp);
- free (file->path);
+ free (file->bare);
+ free (file->extension);
free (file);
}
+#ifdef OS_UNIX
+
+static int
+on_unique_try_link (void *data,
+ char *path)
+{
+ p11_save_file *file = data;
+
+ if (link (file->temp, path) < 0) {
+ if (errno == EEXIST)
+ return 0; /* Continue trying other names */
+ p11_message ("couldn't complete writing of file: %s: %s",
+ path, strerror (errno));
+ return -1;
+ }
+
+ return 1; /* All done */
+}
+
+#else /* OS_WIN32 */
+
+static int
+on_unique_try_rename (void *data,
+ char *path)
+{
+ p11_save_file *file = data;
+
+ if (rename (file->temp, path) < 0) {
+ if (errno == EEXIST)
+ return 0; /* Continue trying other names */
+ p11_message ("couldn't complete writing of file: %s: %s",
+ path, strerror (errno));
+ return -1;
+ }
+
+ return 1; /* All done */
+}
+
+#endif /* OS_WIN32 */
+
bool
p11_save_finish_file (p11_save_file *file,
+ char **path_out,
bool commit)
{
bool ret = true;
+ char *path;
if (!file)
return false;
return true;
}
+ if (asprintf (&path, "%s%s", file->bare, file->extension) < 0)
+ return_val_if_reached (false);
+
if (close (file->fd) < 0) {
p11_message ("couldn't write file: %s: %s",
file->temp, strerror (errno));
/* Atomically rename the tempfile over the filename */
} else if (file->flags & P11_SAVE_OVERWRITE) {
- if (rename (file->temp, file->path) < 0) {
+ if (rename (file->temp, path) < 0) {
p11_message ("couldn't complete writing file: %s: %s",
- file->path, strerror (errno));
+ path, strerror (errno));
ret = false;
} else {
unlink (file->temp);
}
+ /* Create a unique name if requested unique file name */
+ } else if (file->flags & P11_SAVE_UNIQUE) {
+ free (path);
+ path = make_unique_name (file->bare, file->extension,
+ on_unique_try_link, file);
+ if (!path)
+ ret = false;
+ unlink (file->temp);
+
/* When not overwriting, link will fail if filename exists. */
} else {
- if (link (file->temp, file->path) < 0) {
+ if (link (file->temp, path) < 0) {
p11_message ("couldn't complete writing of file: %s: %s",
- file->path, strerror (errno));
+ path, strerror (errno));
ret = false;
}
unlink (file->temp);
/* Windows does not do atomic renames, so delete original file first */
} else {
- if (file->flags & P11_SAVE_OVERWRITE) {
- if (unlink (file->path) < 0 && errno != ENOENT) {
+ /* Create a unique name if requested unique file name */
+ if (file->flags & P11_SAVE_UNIQUE) {
+ free (path);
+ path = make_unique_name (file->bare, file->extension,
+ on_unique_try_rename, file);
+ if (!path)
+ ret = false;
+
+ } else {
+ if ((file->flags & P11_SAVE_OVERWRITE) &&
+ unlink (path) < 0 && errno != ENOENT) {
p11_message ("couldn't remove original file: %s: %s",
- file->path, strerror (errno));
+ path, strerror (errno));
ret = false;
}
- }
- if (ret == true) {
- if (rename (file->temp, file->path) < 0) {
+ if (ret == true &&
+ rename (file->temp, file->path) < 0) {
p11_message ("couldn't complete writing file: %s: %s",
file->path, strerror (errno));
ret = false;
#endif /* OS_WIN32 */
}
+ if (ret && path_out) {
+ *path_out = path;
+ path = NULL;
+ }
+
+ free (path);
filo_free (file);
return ret;
}
}
static char *
-make_unique_name (p11_save_dir *dir,
- const char *filename,
- const char *extension)
+make_unique_name (const char *bare,
+ const char *extension,
+ int (*check) (void *, char *),
+ void *data)
{
char unique[16];
p11_buffer buf;
+ int ret;
int i;
+ assert (bare != NULL);
+ assert (check != NULL);
+
p11_buffer_init_null (&buf, 0);
for (i = 0; true; i++) {
* provided by the caller.
*/
case 0:
- p11_buffer_add (&buf, filename, -1);
+ p11_buffer_add (&buf, bare, -1);
break;
/*
/* fall through */
default:
- p11_buffer_add (&buf, filename, -1);
+ p11_buffer_add (&buf, bare, -1);
snprintf (unique, sizeof (unique), ".%d", i);
p11_buffer_add (&buf, unique, -1);
break;
return_val_if_fail (p11_buffer_ok (&buf), NULL);
- if (!p11_dict_get (dir->cache, buf.data))
+ ret = check (data, buf.data);
+ if (ret < 0)
+ return NULL;
+ else if (ret > 0)
return p11_buffer_steal (&buf, NULL);
}
assert_not_reached ();
}
+static int
+on_unique_check_dir (void *data,
+ char *name)
+{
+ p11_save_dir *dir = data;
+
+ if (!p11_dict_get (dir->cache, name))
+ return 1;
+
+ return 0; /* Keep looking */
+}
+
p11_save_file *
p11_save_open_file_in (p11_save_dir *dir,
const char *basename,
- const char *extension,
- const char **ret_name)
+ const char *extension)
{
p11_save_file *file = NULL;
char *name;
return_val_if_fail (dir != NULL, NULL);
return_val_if_fail (basename != NULL, NULL);
- name = make_unique_name (dir, basename, extension);
+ name = make_unique_name (basename, extension, on_unique_check_dir, dir);
return_val_if_fail (name != NULL, NULL);
if (asprintf (&path, "%s/%s", dir->path, name) < 0)
return_val_if_reached (NULL);
- file = p11_save_open_file (path, dir->flags);
+ file = p11_save_open_file (path, NULL, dir->flags);
if (file) {
if (!p11_dict_set (dir->cache, name, name))
return_val_if_reached (NULL);
- if (ret_name)
- *ret_name = name;
name = NULL;
}
return_val_if_fail (linkname != NULL, false);
return_val_if_fail (destination != NULL, false);
- name = make_unique_name (dir, linkname, extension);
+ name = make_unique_name (linkname, extension, on_unique_check_dir, dir);
return_val_if_fail (name != NULL, false);
if (asprintf (&path, "%s/%s", dir->path, name) < 0)
#include <sys/stat.h>
#include <sys/types.h>
-#include <assert.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
if (asprintf (&filename, "%s/%s", test.directory, "extract-file") < 0)
assert_not_reached ();
- file = p11_save_open_file (filename, 0);
+ file = p11_save_open_file (filename, NULL, 0);
assert_ptr_not_null (file);
ret = p11_save_write_and_finish (file, test_cacert3_ca_der, sizeof (test_cacert3_ca_der));
p11_message_quiet ();
- file = p11_save_open_file (filename, 0);
- assert (file == NULL);
+ file = p11_save_open_file (filename, NULL, 0);
+ assert (file != NULL);
+
+ if (p11_save_finish_file (file, NULL, true))
+ assert_not_reached ();
p11_message_loud ();
p11_message_quiet ();
- file = p11_save_open_file (filename, 0);
+ file = p11_save_open_file (filename, NULL, 0);
assert (file == NULL);
p11_message_loud ();
write_zero_file (test.directory, "extract-file");
- file = p11_save_open_file (filename, P11_SAVE_OVERWRITE);
+ file = p11_save_open_file (filename, NULL, P11_SAVE_OVERWRITE);
assert_ptr_not_null (file);
ret = p11_save_write_and_finish (file, test_cacert3_ca_der, sizeof (test_cacert3_ca_der));
test_check_file (test.directory, "extract-file", SRCDIR "/files/cacert3.der");
}
+static void
+test_file_unique (void)
+{
+ p11_save_file *file;
+ char *filename;
+ bool ret;
+
+ if (asprintf (&filename, "%s/%s", test.directory, "extract-file") < 0)
+ assert_not_reached ();
+
+ write_zero_file (test.directory, "extract-file");
+
+ file = p11_save_open_file (filename, NULL, P11_SAVE_UNIQUE);
+ assert_ptr_not_null (file);
+
+ ret = p11_save_write_and_finish (file, test_cacert3_ca_der, sizeof (test_cacert3_ca_der));
+ assert_num_eq (true, ret);
+ free (filename);
+
+ test_check_file (test.directory, "extract-file", SRCDIR "/files/empty-file");
+ test_check_file (test.directory, "extract-file.1", SRCDIR "/files/cacert3.der");
+}
+
static void
test_file_auto_empty (void)
{
if (asprintf (&filename, "%s/%s", test.directory, "extract-file") < 0)
assert_not_reached ();
- file = p11_save_open_file (filename, 0);
+ file = p11_save_open_file (filename, NULL, 0);
assert_ptr_not_null (file);
ret = p11_save_write_and_finish (file, NULL, -1);
if (asprintf (&filename, "%s/%s", test.directory, "extract-file") < 0)
assert_not_reached ();
- file = p11_save_open_file (filename, 0);
+ file = p11_save_open_file (filename, NULL, 0);
assert_ptr_not_null (file);
ret = p11_save_write_and_finish (file, "The simple string is hairy", -1);
struct stat st;
p11_save_file *file;
char *filename;
+ char *path;
bool ret;
if (asprintf (&filename, "%s/%s", test.directory, "extract-file") < 0)
assert_not_reached ();
- file = p11_save_open_file (filename, 0);
+ file = p11_save_open_file (filename, NULL, 0);
assert_ptr_not_null (file);
- ret = p11_save_finish_file (file, false);
+ path = NULL;
+ ret = p11_save_finish_file (file, &path, false);
assert_num_eq (true, ret);
+ assert (path == NULL);
if (stat (filename, &st) >= 0 || errno != ENOENT)
assert_fail ("file should not exist", filename);
static void
test_directory_files (void)
{
- const char *filename;
+ char *path;
+ char *check;
+ p11_save_file *file;
p11_save_dir *dir;
char *subdir;
bool ret;
dir = p11_save_open_directory (subdir, 0);
assert_ptr_not_null (dir);
- ret = p11_save_write_and_finish (p11_save_open_file_in (dir, "blah", ".cer", &filename),
- test_cacert3_ca_der, sizeof (test_cacert3_ca_der));
+ file = p11_save_open_file_in (dir, "blah", ".cer");
+ assert_ptr_not_null (file);
+ ret = p11_save_write (file, test_cacert3_ca_der, sizeof (test_cacert3_ca_der));
+ assert_num_eq (true, ret);
+ ret = p11_save_finish_file (file, &path, true);
assert_num_eq (true, ret);
- assert_str_eq ("blah.cer", filename);
+ if (asprintf (&check, "%s/%s", subdir, "blah.cer") < 0)
+ assert_not_reached ();
+ assert_str_eq (check, path);
+ free (check);
+ free (path);
- ret = p11_save_write_and_finish (p11_save_open_file_in (dir, "file", ".txt", &filename),
- test_text, strlen (test_text));
+ file = p11_save_open_file_in (dir, "file", ".txt");
+ assert_ptr_not_null (file);
+ ret = p11_save_write (file, test_text, strlen (test_text));
+ assert_num_eq (true, ret);
+ ret = p11_save_finish_file (file, &path, true);
assert_num_eq (true, ret);
- assert_str_eq ("file.txt", filename);
+ if (asprintf (&check, "%s/%s", subdir, "file.txt") < 0)
+ assert_not_reached ();
+ assert_str_eq (check, path);
+ free (check);
+ free (path);
#ifdef OS_UNIX
ret = p11_save_symlink_in (dir, "link", ".ext", "/the/destination");
static void
test_directory_dups (void)
{
- const char *filename;
+ char *path;
+ char *check;
+ p11_save_file *file;
p11_save_dir *dir;
char *subdir;
bool ret;
dir = p11_save_open_directory (subdir, 0);
assert_ptr_not_null (dir);
- ret = p11_save_write_and_finish (p11_save_open_file_in (dir, "file", ".txt", &filename),
- test_text, 5);
+ file = p11_save_open_file_in (dir, "file", ".txt");
+ assert_ptr_not_null (file);
+ ret = p11_save_write (file, test_text, 5);
+ assert_num_eq (true, ret);
+ ret = p11_save_finish_file (file, &path, true);
assert_num_eq (true, ret);
- assert_str_eq ("file.txt", filename);
+ if (asprintf (&check, "%s/%s", subdir, "file.txt") < 0)
+ assert_not_reached ();
+ assert_str_eq (check, path);
+ free (check);
+ free (path);
- ret = p11_save_write_and_finish (p11_save_open_file_in (dir, "file", ".txt", &filename),
- test_text, 10);
+ file = p11_save_open_file_in (dir, "file", ".txt");
+ assert_ptr_not_null (file);
+ ret = p11_save_write (file, test_text, 10);
+ assert_num_eq (true, ret);
+ ret = p11_save_finish_file (file, &path, true);
assert_num_eq (true, ret);
- assert_str_eq ("file.1.txt", filename);
+ if (asprintf (&check, "%s/%s", subdir, "file.1.txt") < 0)
+ assert_not_reached ();
+ assert_str_eq (check, path);
+ free (check);
+ free (path);
- ret = p11_save_write_and_finish (p11_save_open_file_in (dir, "file", ".txt", NULL),
+ ret = p11_save_write_and_finish (p11_save_open_file_in (dir, "file", ".txt"),
test_text, 15);
assert_num_eq (true, ret);
- ret = p11_save_write_and_finish (p11_save_open_file_in (dir, "no-ext", NULL, NULL),
+ ret = p11_save_write_and_finish (p11_save_open_file_in (dir, "no-ext", NULL),
test_text, 8);
assert_num_eq (true, ret);
- ret = p11_save_write_and_finish (p11_save_open_file_in (dir, "no-ext", NULL, NULL),
+ ret = p11_save_write_and_finish (p11_save_open_file_in (dir, "no-ext", NULL),
test_text, 16);
assert_num_eq (true, ret);
- ret = p11_save_write_and_finish (p11_save_open_file_in (dir, "with-num", ".0", NULL),
+ ret = p11_save_write_and_finish (p11_save_open_file_in (dir, "with-num", ".0"),
test_text, 14);
assert_num_eq (true, ret);
- ret = p11_save_write_and_finish (p11_save_open_file_in (dir, "with-num", ".0", NULL),
+ ret = p11_save_write_and_finish (p11_save_open_file_in (dir, "with-num", ".0"),
test_text, 15);
assert_num_eq (true, ret);
static void
test_directory_overwrite (void)
{
- const char *filename;
+ char *path;
+ char *check;
+ p11_save_file *file;
p11_save_dir *dir;
char *subdir;
bool ret;
/* Some initial files into this directory, which get overwritten */
dir = p11_save_open_directory (subdir, 0);
- ret = p11_save_write_and_finish (p11_save_open_file_in (dir, "file", ".txt", NULL), "", 0) &&
- p11_save_write_and_finish (p11_save_open_file_in (dir, "another-file", NULL, NULL), "", 0) &&
- p11_save_write_and_finish (p11_save_open_file_in (dir, "third-file", NULL, NULL), "", 0) &&
+ ret = p11_save_write_and_finish (p11_save_open_file_in (dir, "file", ".txt"), "", 0) &&
+ p11_save_write_and_finish (p11_save_open_file_in (dir, "another-file", NULL), "", 0) &&
+ p11_save_write_and_finish (p11_save_open_file_in (dir, "third-file", NULL), "", 0) &&
p11_save_finish_directory (dir, true);
assert (ret && dir);
dir = p11_save_open_directory (subdir, P11_SAVE_OVERWRITE);
assert_ptr_not_null (dir);
- ret = p11_save_write_and_finish (p11_save_open_file_in (dir, "blah", ".cer", &filename),
- test_cacert3_ca_der, sizeof (test_cacert3_ca_der));
+ file = p11_save_open_file_in (dir, "blah", ".cer");
+ assert_ptr_not_null (file);
+ ret = p11_save_write (file, test_cacert3_ca_der, sizeof (test_cacert3_ca_der));
assert_num_eq (true, ret);
- assert_str_eq ("blah.cer", filename);
+ ret = p11_save_finish_file (file, &path, true);
+ assert_num_eq (true, ret);
+ if (asprintf (&check, "%s/%s", subdir, "blah.cer") < 0)
+ assert_not_reached ();
+ assert_str_eq (check, path);
+ free (check);
+ free (path);
- ret = p11_save_write_and_finish (p11_save_open_file_in (dir, "file", ".txt", &filename),
- test_text, strlen (test_text));
+ file = p11_save_open_file_in (dir, "file", ".txt");
+ assert_ptr_not_null (file);
+ ret = p11_save_write (file, test_text, strlen (test_text));
assert_num_eq (true, ret);
- assert_str_eq ("file.txt", filename);
+ ret = p11_save_finish_file (file, &path, true);
+ assert_num_eq (true, ret);
+ if (asprintf (&check, "%s/%s", subdir, "file.txt") < 0)
+ assert_not_reached ();
+ assert_str_eq (check, path);
+ free (check);
+ free (path);
- ret = p11_save_write_and_finish (p11_save_open_file_in (dir, "file", ".txt", &filename),
- test_text, 10);
+ file = p11_save_open_file_in (dir, "file", ".txt");
+ assert_ptr_not_null (file);
+ ret = p11_save_write (file, test_text, 10);
assert_num_eq (true, ret);
- assert_str_eq ("file.1.txt", filename);
+ ret = p11_save_finish_file (file, &path, true);
+ assert_num_eq (true, ret);
+ if (asprintf (&check, "%s/%s", subdir, "file.1.txt") < 0)
+ assert_not_reached ();
+ assert_str_eq (check, path);
+ free (check);
+ free (path);
ret = p11_save_finish_directory (dir, true);
assert_num_eq (true, ret);
p11_test (test_file_exists, "/save/test_file_exists");
p11_test (test_file_bad_directory, "/save/test_file_bad_directory");
p11_test (test_file_overwrite, "/save/test_file_overwrite");
+ p11_test (test_file_unique, "/save/file-unique");
p11_test (test_file_auto_empty, "/save/test_file_auto_empty");
p11_test (test_file_auto_length, "/save/test_file_auto_length");