1/* Test --no-hard-links option to localedef.
2 Copyright (C) 2020-2022 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19/* The test is designed to run in a container and execute localedef
20 once without --no-hard-links, verify that there are 2 hard links to
21 LC_CTYPE, and then run again *with* --no-hard-links and verify there
22 are no hard links and link counts remain at 1. The expectation here
23 is that LC_CTYPE is identical for both locales because they are same
24 empty locale but with a different name. We use tests-container in
25 this test because the hard link optimziation is only carried out for
26 the default locale installation directory, and to write to that we
27 need write access to that directory, enabled by 'su' via
28 tests-container framework. */
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <sys/stat.h>
33#include <sys/types.h>
34#include <sys/wait.h>
35
36#include <support/check.h>
37#include <support/support.h>
38#include <support/xunistd.h>
39#include <support/capture_subprocess.h>
40
41/* Each test compiles a locale to output, and has an expected link count
42 for LC_CTYPE. This test expects that localedef removes the existing
43 files before installing new copies of the files, and we do not
44 cleanup between localedef runs. We can't cleanup between each pair
45 of runs since localedef must see the existing locale in order to
46 determine that space could be saved by using a hardlink. */
47struct test_data
48{
49 /* Arguments to localedef for this step. */
50 const char * argv[16];
51 /* Expected output file generated by running localedef. */
52 const char *output;
53 /* Expected st_nlink count for the output. */
54 int st_nlink;
55};
56
57/* Check for link count. */
58void
59check_link (struct test_data step)
60{
61 struct stat64 locale;
62 char *output;
63
64 output = xasprintf (format: "%s/%s", support_complocaledir_prefix, step.output);
65 xstat (path: output, &locale);
66 free (ptr: output);
67 TEST_COMPARE (locale.st_nlink, step.st_nlink);
68}
69
70static void
71run_localedef (void *step)
72{
73 const char *prog = xasprintf (format: "%s/localedef", support_bindir_prefix);
74 struct test_data *one = (struct test_data *) step;
75
76 one->argv[0] = prog;
77 execv (path: prog, argv: (char * const *) one->argv);
78 FAIL_EXIT1 ("execv: %m");
79}
80
81#define TEST1DIR "test1_locale.dir"
82#define TEST2DIR "test2_locale.dir"
83
84/* The whole test has 4 steps described below. Note the argv[0] NULL
85 will be filled in at runtime by run_localedef. */
86static struct test_data step[4] = {
87 { .argv = { NULL, "--no-archive", "-i", "/test1_locale", TEST1DIR, NULL },
88 .output = TEST1DIR "/LC_CTYPE",
89 .st_nlink = 1 },
90 { .argv = { NULL, "--no-archive", "-i", "/test2_locale", TEST2DIR, NULL },
91 .output = TEST2DIR "/LC_CTYPE",
92 .st_nlink = 2 },
93 { .argv = { NULL, "--no-archive", "--no-hard-links", "-i", "/test1_locale",
94 TEST1DIR, NULL },
95 .output = TEST1DIR "/LC_CTYPE",
96 .st_nlink = 1 },
97 { .argv = { NULL, "--no-archive", "--no-hard-links", "-i", "/test2_locale",
98 TEST1DIR, NULL },
99 .output = TEST2DIR "/LC_CTYPE",
100 .st_nlink = 1 },
101};
102
103static int
104do_test (void)
105{
106 struct support_capture_subprocess result;
107
108 printf (format: "INFO: $complocaledir is %s\n", support_complocaledir_prefix);
109 /* Compile the first locale. */
110 result = support_capture_subprocess (callback: run_localedef, closure: (void *) &step[0]);
111 support_capture_subprocess_check (&result, context: "execv", status_or_signal: 1, allowed: sc_allow_stderr);
112 check_link (step: step[0]);
113
114 /* This time around we should have link counts of 2 for the second
115 linked locale since categories are identical. */
116 result = support_capture_subprocess (callback: run_localedef, closure: (void *) &step[1]);
117 support_capture_subprocess_check (&result, context: "execv", status_or_signal: 1, allowed: sc_allow_stderr);
118 check_link (step: step[1]);
119
120 /* Again with --no-hard-links (link count is always one). */
121 result = support_capture_subprocess (callback: run_localedef, closure: (void *) &step[2]);
122 support_capture_subprocess_check (&result, context: "execv", status_or_signal: 1, allowed: sc_allow_stderr);
123 check_link (step: step[2]);
124
125 /* Again with --no-hard-links, and the link count must remain 1. */
126 result = support_capture_subprocess (callback: run_localedef, closure: (void *) &step[3]);
127 support_capture_subprocess_check (&result, context: "execv", status_or_signal: 1, allowed: sc_allow_stderr);
128 check_link (step: step[3]);
129
130 /* Tested without and with --no-hard-links and link counts were
131 consistent. */
132 return EXIT_SUCCESS;
133}
134
135#include <support/test-driver.c>
136

source code of glibc/localedata/tst-localedef-hardlinks.c