Skip to content

Commit ad0906b

Browse files
committed
builder-utils: Rewrite locale migration to use fd-based traversal
1 parent 1b4e45d commit ad0906b

1 file changed

Lines changed: 208 additions & 59 deletions

File tree

src/builder-utils.c

Lines changed: 208 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -283,78 +283,228 @@ directory_is_empty (const char *path)
283283
return empty;
284284
}
285285

286+
static void
287+
locale_name_to_language (char *name)
288+
{
289+
char *c;
290+
291+
if ((c = strchr (name, '@'))) *c = '\0';
292+
if ((c = strchr (name, '_'))) *c = '\0';
293+
if ((c = strchr (name, '.'))) *c = '\0';
294+
}
295+
286296
static gboolean
287-
migrate_locale_dir (GFile *source_dir,
288-
GFile *separate_dir,
289-
const char *subdir,
290-
GError **error)
297+
migrate_locale_dir_contents (int src_dfd,
298+
int dst_dfd,
299+
GError **error)
291300
{
292-
g_autoptr(GFileEnumerator) dir_enum = NULL;
293-
GFileInfo *next;
294-
GError *temp_error = NULL;
301+
g_auto(GLnxDirFdIterator) iter = { 0, };
302+
struct dirent *dent;
295303

296-
dir_enum = g_file_enumerate_children (source_dir, "standard::name,standard::type",
297-
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
298-
NULL, NULL);
299-
if (!dir_enum)
300-
return TRUE;
304+
if (!glnx_dirfd_iterator_init_at (src_dfd, ".", FALSE, &iter, error))
305+
return FALSE;
301306

302-
while ((next = g_file_enumerator_next_file (dir_enum, NULL, &temp_error)))
307+
while (TRUE)
303308
{
304-
g_autoptr(GFileInfo) child_info = next;
305-
g_autoptr(GFile) locale_subdir = NULL;
309+
glnx_autofd int chase_fd = -1;
310+
struct glnx_statx stx;
311+
char tmp_name[] = ".locale-XXXXXX";
306312

307-
if (g_file_info_get_file_type (child_info) == G_FILE_TYPE_DIRECTORY)
313+
if (!glnx_dirfd_iterator_next_dent (&iter, &dent, NULL, error))
314+
return FALSE;
315+
316+
if (dent == NULL)
317+
break;
318+
319+
chase_fd = glnx_chase_and_statxat (src_dfd, dent->d_name,
320+
GLNX_CHASE_NOFOLLOW |
321+
GLNX_CHASE_RESOLVE_BENEATH,
322+
GLNX_STATX_TYPE,
323+
&stx,
324+
error);
325+
if (chase_fd < 0)
326+
return FALSE;
327+
328+
glnx_gen_temp_name (tmp_name);
329+
330+
if (!glnx_renameat (src_dfd, dent->d_name, src_dfd, tmp_name, error))
331+
return FALSE;
332+
333+
if (S_ISREG (stx.stx_mode) || S_ISLNK (stx.stx_mode))
308334
{
309-
g_autoptr(GFile) child = NULL;
310-
const char *name = g_file_info_get_name (child_info);
311-
g_autofree char *language = g_strdup (name);
312-
g_autofree char *relative = NULL;
313-
g_autofree char *target = NULL;
314-
char *c;
315-
316-
c = strchr (language, '@');
317-
if (c != NULL)
318-
*c = 0;
319-
c = strchr (language, '_');
320-
if (c != NULL)
321-
*c = 0;
322-
c = strchr (language, '.');
323-
if (c != NULL)
324-
*c = 0;
325-
326-
/* We ship english and C locales always */
327-
if (strcmp (language, "C") == 0 ||
328-
strcmp (language, "en") == 0)
329-
continue;
330-
331-
child = g_file_get_child (source_dir, g_file_info_get_name (child_info));
332-
333-
relative = g_build_filename (language, subdir, name, NULL);
334-
locale_subdir = g_file_resolve_relative_path (separate_dir, relative);
335-
if (!flatpak_mkdir_p (locale_subdir, NULL, error))
335+
if (!glnx_file_copy_at (src_dfd, tmp_name, NULL,
336+
dst_dfd, dent->d_name,
337+
GLNX_FILE_COPY_OVERWRITE |
338+
GLNX_FILE_COPY_NOCHOWN |
339+
GLNX_FILE_COPY_NOXATTRS,
340+
NULL, error))
336341
return FALSE;
337342

338-
if (!flatpak_cp_a (child, locale_subdir, NULL,
339-
FLATPAK_CP_FLAGS_MERGE | FLATPAK_CP_FLAGS_MOVE,
340-
NULL, NULL, error))
343+
if (unlinkat (src_dfd, tmp_name, 0) < 0)
344+
return glnx_throw_errno_prefix (error, "unlinkat %s", tmp_name);
345+
}
346+
else if (S_ISDIR (stx.stx_mode))
347+
{
348+
glnx_autofd int src_child_dfd = -1;
349+
glnx_autofd int child_dst_dfd = -1;
350+
351+
src_child_dfd = glnx_fd_reopen (chase_fd, O_RDONLY | O_DIRECTORY, error);
352+
if (src_child_dfd < 0)
341353
return FALSE;
342354

343-
target = g_build_filename ("../../share/runtime/locale", relative, NULL);
355+
if (!glnx_ensure_dir (dst_dfd, dent->d_name, 0755, error))
356+
return FALSE;
344357

345-
if (!g_file_make_symbolic_link (child, target,
346-
NULL, error))
358+
if (!glnx_opendirat (dst_dfd, dent->d_name, FALSE,
359+
&child_dst_dfd, error))
347360
return FALSE;
348361

362+
if (!migrate_locale_dir_contents (src_child_dfd, child_dst_dfd, error))
363+
return FALSE;
364+
365+
if (unlinkat (src_dfd, tmp_name, AT_REMOVEDIR) < 0)
366+
return glnx_throw_errno_prefix (error, "unlinkat %s", tmp_name);
349367
}
368+
else
369+
return glnx_throw (error, "unexpected entry type %s", dent->d_name);
350370
}
371+
return TRUE;
372+
}
373+
374+
static gboolean
375+
migrate_lang_dir (int source_dfd,
376+
const char *lang_name,
377+
const char *src_tmp_name,
378+
int lang_dfd,
379+
int separate_dfd,
380+
const char *subdir,
381+
GError **error)
382+
{
383+
glnx_autofd int locale_subdir_dfd = -1;
384+
g_autofree char *language = g_strdup (lang_name);
385+
g_autofree char *target = NULL;
386+
387+
locale_name_to_language (language);
388+
389+
const char *components[] = { language, subdir, lang_name, NULL };
390+
391+
if (!builder_ensure_dirs_at (separate_dfd, components, &locale_subdir_dfd, error))
392+
return FALSE;
393+
394+
if (!migrate_locale_dir_contents (lang_dfd, locale_subdir_dfd, error))
395+
return FALSE;
396+
397+
target = g_build_filename ("../../share/runtime/locale",
398+
language, subdir, lang_name, NULL);
399+
400+
if (unlinkat (source_dfd, src_tmp_name, AT_REMOVEDIR) < 0)
401+
return glnx_throw_errno_prefix (error, "unlinkat %s", src_tmp_name);
402+
403+
if (symlinkat (target, source_dfd, lang_name) < 0)
404+
return glnx_throw_errno_prefix (error, "symlinkat %s", lang_name);
405+
406+
return TRUE;
407+
}
351408

352-
if (temp_error != NULL)
409+
static gboolean
410+
migrate_locale_dir (int root_dfd,
411+
const char *source_rel,
412+
int root_dfd_separate,
413+
const char *subdir,
414+
GError **error)
415+
{
416+
glnx_autofd int chase_fd = -1;
417+
glnx_autofd int source_dfd = -1;
418+
glnx_autofd int separate_dfd = -1;
419+
g_auto(GLnxDirFdIterator) iter = { 0, };
420+
struct dirent *dent;
421+
const char *separate_components[] = { "share", "runtime", "locale", NULL };
422+
g_autoptr(GPtrArray) names = g_ptr_array_new_with_free_func (g_free);
423+
424+
chase_fd = glnx_chaseat (root_dfd, source_rel,
425+
GLNX_CHASE_RESOLVE_BENEATH |
426+
GLNX_CHASE_MUST_BE_DIRECTORY,
427+
error);
428+
if (chase_fd < 0)
353429
{
354-
g_propagate_error (error, temp_error);
430+
if (g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
431+
{
432+
g_clear_error (error);
433+
return TRUE;
434+
}
355435
return FALSE;
356436
}
357437

438+
source_dfd = glnx_fd_reopen (chase_fd, O_RDONLY | O_DIRECTORY, error);
439+
if (source_dfd < 0)
440+
return glnx_prefix_error (error, "failed to reopen %s", source_rel);
441+
442+
if (!glnx_dirfd_iterator_init_at (source_dfd, ".", FALSE, &iter, error))
443+
return FALSE;
444+
445+
while (TRUE)
446+
{
447+
if (!glnx_dirfd_iterator_next_dent (&iter, &dent, NULL, error))
448+
return FALSE;
449+
450+
if (dent == NULL)
451+
break;
452+
453+
{
454+
g_autofree char *language = g_strdup (dent->d_name);
455+
locale_name_to_language (language);
456+
457+
/* We ship english and C locales always */
458+
if (strcmp (language, "C") == 0 ||
459+
strcmp (language, "en") == 0)
460+
continue;
461+
}
462+
463+
g_ptr_array_add (names, g_strdup (dent->d_name));
464+
}
465+
466+
if (names->len == 0)
467+
return TRUE;
468+
469+
if (!builder_ensure_dirs_at (root_dfd_separate, separate_components,
470+
&separate_dfd, error))
471+
return FALSE;
472+
473+
for (size_t i = 0; i < names->len; i++)
474+
{
475+
const char *name = names->pdata[i];
476+
glnx_autofd int lang_dfd = -1;
477+
glnx_autofd int reopened_lang_dfd = -1;
478+
char tmp_name[] = ".locale-XXXXXX";
479+
480+
lang_dfd = glnx_chaseat (source_dfd, name,
481+
GLNX_CHASE_RESOLVE_BENEATH |
482+
GLNX_CHASE_MUST_BE_DIRECTORY,
483+
error);
484+
if (lang_dfd < 0)
485+
{
486+
if (g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY))
487+
{
488+
g_clear_error (error);
489+
continue;
490+
}
491+
return FALSE;
492+
}
493+
494+
reopened_lang_dfd = glnx_fd_reopen (lang_dfd, O_RDONLY | O_DIRECTORY, error);
495+
if (reopened_lang_dfd < 0)
496+
return glnx_prefix_error (error, "failed to reopen %s", name);
497+
498+
glnx_gen_temp_name (tmp_name);
499+
500+
if (!glnx_renameat (source_dfd, name, source_dfd, tmp_name, error))
501+
return FALSE;
502+
503+
if (!migrate_lang_dir (source_dfd, name, tmp_name, reopened_lang_dfd,
504+
separate_dfd, subdir, error))
505+
return FALSE;
506+
}
507+
358508
return TRUE;
359509
}
360510

@@ -395,18 +545,17 @@ gboolean
395545
builder_migrate_locale_dirs (GFile *root_dir,
396546
GError **error)
397547
{
398-
g_autoptr(GFile) separate_dir = NULL;
399-
g_autoptr(GFile) lib_locale_dir = NULL;
400-
g_autoptr(GFile) share_locale_dir = NULL;
548+
glnx_autofd int root_dfd = -1;
401549

402-
lib_locale_dir = g_file_resolve_relative_path (root_dir, "lib/locale");
403-
share_locale_dir = g_file_resolve_relative_path (root_dir, "share/locale");
404-
separate_dir = g_file_resolve_relative_path (root_dir, "share/runtime/locale");
550+
if (!glnx_opendirat (AT_FDCWD,
551+
flatpak_file_get_path_cached (root_dir),
552+
FALSE, &root_dfd, error))
553+
return FALSE;
405554

406-
if (!migrate_locale_dir (lib_locale_dir, separate_dir, "lib", error))
555+
if (!migrate_locale_dir (root_dfd, "lib/locale", root_dfd, "lib", error))
407556
return FALSE;
408557

409-
if (!migrate_locale_dir (share_locale_dir, separate_dir, "share", error))
558+
if (!migrate_locale_dir (root_dfd, "share/locale", root_dfd, "share", error))
410559
return FALSE;
411560

412561
return TRUE;

0 commit comments

Comments
 (0)