mirror of
https://github.com/ostreedev/ostree.git
synced 2025-01-11 09:18:20 +03:00
repo: Add _ostree_repo_allocate_tmpdir helper
This creates a subdirectory of the tmp dir with a selected prefix, and takes a lockfile to ensure that nobody else is using the same directory. However, if a directory with the same prefix already exists and is not locked that is used instead. The later is useful if you want to support some kind of resumed operation on the tmpdir. touch reused dirs https://bugzilla.gnome.org/show_bug.cgi?id=757611
This commit is contained in:
parent
211ca6795c
commit
be19c88861
@ -90,6 +90,16 @@ struct OstreeRepo {
|
||||
OstreeRepo *parent_repo;
|
||||
};
|
||||
|
||||
gboolean
|
||||
_ostree_repo_allocate_tmpdir (int tmpdir_dfd,
|
||||
const char *tmpdir_prefix,
|
||||
char **tmpdir_name_out,
|
||||
int *tmpdir_fd_out,
|
||||
GLnxLockFile *file_lock_out,
|
||||
gboolean * reusing_dir_out,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
gboolean
|
||||
_ostree_repo_ensure_loose_objdir_at (int dfd,
|
||||
const char *loose_path,
|
||||
|
@ -4617,3 +4617,134 @@ ostree_repo_regenerate_summary (OstreeRepo *self,
|
||||
g_list_free (ordered_keys);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* This allocates and locks a subdir of the repo tmp dir, using an existing
|
||||
* one with the same prefix if it is not in use already. */
|
||||
gboolean
|
||||
_ostree_repo_allocate_tmpdir (int tmpdir_dfd,
|
||||
const char *tmpdir_prefix,
|
||||
char **tmpdir_name_out,
|
||||
int *tmpdir_fd_out,
|
||||
GLnxLockFile *file_lock_out,
|
||||
gboolean *reusing_dir_out,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
gboolean reusing_dir = FALSE;
|
||||
g_autofree char *tmpdir_name = NULL;
|
||||
glnx_fd_close int tmpdir_fd = -1;
|
||||
g_auto(GLnxDirFdIterator) dfd_iter = { 0, };
|
||||
|
||||
/* Look for existing tmpdir (with same prefix) to reuse */
|
||||
if (!glnx_dirfd_iterator_init_at (tmpdir_dfd, ".", FALSE, &dfd_iter, error))
|
||||
return FALSE;
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
gs_dirfd_iterator_cleanup GSDirFdIterator child_dfd_iter = { 0, };
|
||||
struct dirent *dent;
|
||||
glnx_fd_close int existing_tmpdir_fd = -1;
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
g_autofree char *lock_name = NULL;
|
||||
|
||||
if (!glnx_dirfd_iterator_next_dent (&dfd_iter, &dent, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (dent == NULL)
|
||||
break;
|
||||
|
||||
if (!g_str_has_prefix (dent->d_name, tmpdir_prefix))
|
||||
continue;
|
||||
|
||||
/* Quickly skip non-dirs, if unknown we ignore ENOTDIR when opening instead */
|
||||
if (dent->d_type != DT_UNKNOWN &&
|
||||
dent->d_type != DT_DIR)
|
||||
continue;
|
||||
|
||||
if (!glnx_opendirat (dfd_iter.fd, dent->d_name, FALSE,
|
||||
&existing_tmpdir_fd, &local_error))
|
||||
{
|
||||
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY))
|
||||
continue;
|
||||
else
|
||||
{
|
||||
g_propagate_error (error, g_steal_pointer (&local_error));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
lock_name = g_strconcat (dent->d_name, "-lock", NULL);
|
||||
|
||||
/* We put the lock outside the dir, so we can hold the lock
|
||||
* until the directory is fully removed */
|
||||
if (!glnx_make_lock_file (dfd_iter.fd, lock_name, LOCK_EX | LOCK_NB,
|
||||
file_lock_out, &local_error))
|
||||
{
|
||||
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
|
||||
continue;
|
||||
else
|
||||
{
|
||||
g_propagate_error (error, g_steal_pointer (&local_error));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Touch the reused directory so that we don't accidentally
|
||||
* remove it due to being old when cleaning up the tmpdir
|
||||
*/
|
||||
(void)futimens (existing_tmpdir_fd, NULL);
|
||||
|
||||
/* We found an existing tmpdir which we managed to lock */
|
||||
tmpdir_name = g_strdup (dent->d_name);
|
||||
tmpdir_fd = glnx_steal_fd (&existing_tmpdir_fd);
|
||||
reusing_dir = TRUE;
|
||||
}
|
||||
|
||||
while (tmpdir_name == NULL)
|
||||
{
|
||||
g_autofree char *tmpdir_name_template = g_strconcat (tmpdir_prefix, "XXXXXX", NULL);
|
||||
glnx_fd_close int new_tmpdir_fd = -1;
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
g_autofree char *lock_name = NULL;
|
||||
|
||||
/* No existing tmpdir found, create a new */
|
||||
|
||||
if (!glnx_mkdtempat (tmpdir_dfd, tmpdir_name_template, 0777, error))
|
||||
return FALSE;
|
||||
|
||||
if (!glnx_opendirat (tmpdir_dfd, tmpdir_name_template, FALSE,
|
||||
&new_tmpdir_fd, error))
|
||||
return FALSE;
|
||||
|
||||
lock_name = g_strconcat (tmpdir_name_template, "-lock", NULL);
|
||||
|
||||
/* Note, at this point we can race with another process that picks up this
|
||||
* new directory. If that happens we need to retry, making a new directory. */
|
||||
if (!glnx_make_lock_file (tmpdir_dfd, lock_name, LOCK_EX | LOCK_NB,
|
||||
file_lock_out, &local_error))
|
||||
{
|
||||
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
|
||||
continue;
|
||||
else
|
||||
{
|
||||
g_propagate_error (error, g_steal_pointer (&local_error));
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
tmpdir_name = g_steal_pointer (&tmpdir_name_template);
|
||||
tmpdir_fd = glnx_steal_fd (&new_tmpdir_fd);
|
||||
}
|
||||
|
||||
if (tmpdir_name_out)
|
||||
*tmpdir_name_out = g_steal_pointer (&tmpdir_name);
|
||||
|
||||
if (tmpdir_fd_out)
|
||||
*tmpdir_fd_out = glnx_steal_fd (&tmpdir_fd);
|
||||
|
||||
if (reusing_dir_out)
|
||||
*reusing_dir_out = reusing_dir;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user