1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
|
From 45ca77f9e4cfe5fd5814d553502ef99877c9e972 Mon Sep 17 00:00:00 2001
From: David Sterba <dsterba@suse.com>
Date: Thu, 18 Apr 2024 18:57:08 +0200
Subject: [PATCH] btrfs-progs: add basename wrappers for unified semantics
What basename(3) does with the argument depends on _GNU_SOURCE and
inclusion of libgen.h. This is problematic on Musl (1.2.5) as reported.
We want the GNU semantics that does not modify the argument. Common way
to make it portable is to add own helper. This is now implemented in
path_basename() that does not use the libc provided basename but preserves
the semantics. The path_dirname() is just for parity, otherwise same as
dirname().
Sources:
- https://bugs.gentoo.org/926288
- https://git.musl-libc.org/cgit/musl/commit/?id=725e17ed6dff4d0cd22487bb64470881e86a92e7
Issue: #778
Signed-off-by: David Sterba <dsterba@suse.com>
---
cmds/subvolume.c | 26 +++++++++++++-------------
common/device-utils.c | 4 ++--
common/path-utils.c | 28 ++++++++++++++++++++++++++++
common/path-utils.h | 2 ++
4 files changed, 45 insertions(+), 15 deletions(-)
diff --git a/cmds/subvolume.c b/cmds/subvolume.c
index 5d53efe6f..869d7077d 100644
--- a/cmds/subvolume.c
+++ b/cmds/subvolume.c
@@ -24,7 +24,6 @@
#include <string.h>
#include <unistd.h>
#include <errno.h>
-#include <libgen.h>
#include <getopt.h>
#include <dirent.h>
#include <stdbool.h>
@@ -149,7 +148,7 @@ static int create_one_subvolume(const char *dst, struct btrfs_qgroup_inherit *in
int fddst = -1;
char *dupname = NULL;
char *dupdir = NULL;
- char *newname;
+ const char *newname;
char *dstdir;
ret = path_is_dir(dst);
@@ -170,7 +169,7 @@ static int create_one_subvolume(const char *dst, struct btrfs_qgroup_inherit *in
ret = -ENOMEM;
goto out;
}
- newname = basename(dupname);
+ newname = path_basename(dupname);
dupdir = strdup(dst);
if (!dupdir) {
@@ -178,7 +177,7 @@ static int create_one_subvolume(const char *dst, struct btrfs_qgroup_inherit *in
ret = -ENOMEM;
goto out;
}
- dstdir = dirname(dupdir);
+ dstdir = path_dirname(dupdir);
if (!test_issubvolname(newname)) {
error("invalid subvolume name: %s", newname);
@@ -364,7 +363,8 @@ static int cmd_subvolume_delete(const struct cmd_struct *cmd, int argc, char **a
int res, ret = 0;
int cnt;
int fd = -1;
- char *dname, *vname, *cpath;
+ char *dname, *cpath;
+ const char *vname;
char *dupdname = NULL;
char *dupvname = NULL;
char *path = NULL;
@@ -482,9 +482,9 @@ static int cmd_subvolume_delete(const struct cmd_struct *cmd, int argc, char **a
goto out;
}
dupdname = strdup(cpath);
- dname = dirname(dupdname);
+ dname = path_dirname(dupdname);
dupvname = strdup(cpath);
- vname = basename(dupvname);
+ vname = path_basename(dupvname);
free(cpath);
/* When subvolid is passed, <path> will point to the mount point */
@@ -670,7 +670,7 @@ static int cmd_subvolume_snapshot(const struct cmd_struct *cmd, int argc, char *
bool readonly = false;
char *dupname = NULL;
char *dupdir = NULL;
- char *newname;
+ const char *newname;
char *dstdir;
enum btrfs_util_error err;
struct btrfs_ioctl_vol_args_v2 args;
@@ -727,13 +727,13 @@ static int cmd_subvolume_snapshot(const struct cmd_struct *cmd, int argc, char *
if (res > 0) {
dupname = strdup(subvol);
- newname = basename(dupname);
+ newname = path_basename(dupname);
dstdir = dst;
} else {
dupname = strdup(dst);
- newname = basename(dupname);
+ newname = path_basename(dupname);
dupdir = strdup(dst);
- dstdir = dirname(dupdir);
+ dstdir = path_dirname(dupdir);
}
if (!test_issubvolname(newname)) {
@@ -1557,7 +1557,7 @@ static int cmd_subvolume_show(const struct cmd_struct *cmd, int argc, char **arg
struct btrfs_util_subvolume_iterator *iter;
struct btrfs_util_subvolume_info subvol;
char *subvol_path = NULL;
- char *subvol_name = NULL;
+ const char *subvol_name = NULL;
enum btrfs_util_error err;
struct btrfs_qgroup_stats stats;
unsigned int unit_mode;
@@ -1669,7 +1669,7 @@ static int cmd_subvolume_show(const struct cmd_struct *cmd, int argc, char **arg
subvol_path = strdup("/");
subvol_name = "<FS_TREE>";
} else {
- subvol_name = basename(subvol_path);
+ subvol_name = path_basename(subvol_path);
}
if (bconf.output_format == CMD_FORMAT_JSON) {
diff --git a/common/device-utils.c b/common/device-utils.c
index 36108ec4e..d086e9ea2 100644
--- a/common/device-utils.c
+++ b/common/device-utils.c
@@ -343,14 +343,14 @@ static u64 device_get_partition_size_sysfs(const char *dev)
char path[PATH_MAX] = {};
char sysfs[PATH_MAX] = {};
char sizebuf[128] = {};
- char *name = NULL;
+ const char *name = NULL;
int sysfd;
unsigned long long size = 0;
name = realpath(dev, path);
if (!name)
return 0;
- name = basename(path);
+ name = path_basename(path);
ret = path_cat3_out(sysfs, "/sys/class/block", name, "size");
if (ret < 0)
diff --git a/common/path-utils.c b/common/path-utils.c
index 181737c4d..929e5c8fa 100644
--- a/common/path-utils.c
+++ b/common/path-utils.c
@@ -28,6 +28,11 @@
#include <string.h>
#include <errno.h>
#include <ctype.h>
+/*
+ * For dirname() and basename(), but never use basename directly, there's
+ * path_basename() with unified GNU behaviour regardless of the includes and
+ * conditional defines. See basename(3) for more.
+ */
#include <libgen.h>
#include <limits.h>
#include "common/path-utils.h"
@@ -482,3 +487,26 @@ int test_issubvolname(const char *name)
strcmp(name, ".") && strcmp(name, "..");
}
+/*
+ * Unified GNU semantics basename helper, never changing the argument. Always
+ * use this instead of basename().
+ */
+const char *path_basename(const char *path)
+{
+ const char *tmp = strrchr(path, '/');
+
+ /* Special case when the whole path is just "/". */
+ if (path[0] == '/' && path[1] == 0)
+ return path;
+
+ return tmp ? tmp + 1 : path;
+}
+
+/*
+ * Return dirname component of path, may change the argument.
+ * Own helper for parity with path_basename().
+ */
+char *path_dirname(char *path)
+{
+ return dirname(path);
+}
diff --git a/common/path-utils.h b/common/path-utils.h
index 08ae0ff10..697fa6b48 100644
--- a/common/path-utils.h
+++ b/common/path-utils.h
@@ -39,6 +39,8 @@ int path_is_dir(const char *path);
int is_same_loop_file(const char *a, const char *b);
int path_is_reg_or_block_device(const char *filename);
int path_is_in_dir(const char *parent, const char *path);
+const char *path_basename(const char *path);
+char *path_dirname(char *path);
int test_issubvolname(const char *name);
|