@@ -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+
286296static 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
395545builder_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