github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/cmd/libsnap-confine-private/mount-opt.c (about) 1 /* 2 * Copyright (C) 2016 Canonical Ltd 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 3 as 6 * published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 * 16 */ 17 18 #include "mount-opt.h" 19 20 #include <errno.h> 21 #include <limits.h> 22 #include <stdio.h> 23 #include <stdlib.h> 24 #include <string.h> 25 #include <sys/mount.h> 26 27 #include "fault-injection.h" 28 #include "privs.h" 29 #include "string-utils.h" 30 #include "utils.h" 31 32 const char *sc_mount_opt2str(char *buf, size_t buf_size, unsigned long flags) 33 { 34 unsigned long used = 0; 35 sc_string_init(buf, buf_size); 36 37 #define F(FLAG, TEXT) do { \ 38 if (flags & (FLAG)) { \ 39 sc_string_append(buf, buf_size, #TEXT ","); flags ^= (FLAG); \ 40 } \ 41 } while (0) 42 43 F(MS_RDONLY, ro); 44 F(MS_NOSUID, nosuid); 45 F(MS_NODEV, nodev); 46 F(MS_NOEXEC, noexec); 47 F(MS_SYNCHRONOUS, sync); 48 F(MS_REMOUNT, remount); 49 F(MS_MANDLOCK, mand); 50 F(MS_DIRSYNC, dirsync); 51 F(MS_NOATIME, noatime); 52 F(MS_NODIRATIME, nodiratime); 53 if (flags & MS_BIND) { 54 if (flags & MS_REC) { 55 sc_string_append(buf, buf_size, "rbind,"); 56 used |= MS_REC; 57 } else { 58 sc_string_append(buf, buf_size, "bind,"); 59 } 60 flags ^= MS_BIND; 61 } 62 F(MS_MOVE, move); 63 // The MS_REC flag handled separately by affected flags (MS_BIND, 64 // MS_PRIVATE, MS_SLAVE, MS_SHARED) 65 // XXX: kernel has MS_VERBOSE, glibc has MS_SILENT, both use the same constant 66 F(MS_SILENT, silent); 67 F(MS_POSIXACL, acl); 68 F(MS_UNBINDABLE, unbindable); 69 if (flags & MS_PRIVATE) { 70 if (flags & MS_REC) { 71 sc_string_append(buf, buf_size, "rprivate,"); 72 used |= MS_REC; 73 } else { 74 sc_string_append(buf, buf_size, "private,"); 75 } 76 flags ^= MS_PRIVATE; 77 } 78 if (flags & MS_SLAVE) { 79 if (flags & MS_REC) { 80 sc_string_append(buf, buf_size, "rslave,"); 81 used |= MS_REC; 82 } else { 83 sc_string_append(buf, buf_size, "slave,"); 84 } 85 flags ^= MS_SLAVE; 86 } 87 if (flags & MS_SHARED) { 88 if (flags & MS_REC) { 89 sc_string_append(buf, buf_size, "rshared,"); 90 used |= MS_REC; 91 } else { 92 sc_string_append(buf, buf_size, "shared,"); 93 } 94 flags ^= MS_SHARED; 95 } 96 flags ^= used; // this is just for MS_REC 97 F(MS_RELATIME, relatime); 98 F(MS_KERNMOUNT, kernmount); 99 F(MS_I_VERSION, iversion); 100 F(MS_STRICTATIME, strictatime); 101 #ifndef MS_LAZYTIME 102 #define MS_LAZYTIME (1<<25) 103 #endif 104 F(MS_LAZYTIME, lazytime); 105 #ifndef MS_NOSEC 106 #define MS_NOSEC (1 << 28) 107 #endif 108 F(MS_NOSEC, nosec); 109 #ifndef MS_BORN 110 #define MS_BORN (1 << 29) 111 #endif 112 F(MS_BORN, born); 113 F(MS_ACTIVE, active); 114 F(MS_NOUSER, nouser); 115 #undef F 116 // Render any flags that are unaccounted for. 117 if (flags) { 118 char of[128] = { 0 }; 119 sc_must_snprintf(of, sizeof of, "%#lx", flags); 120 sc_string_append(buf, buf_size, of); 121 } 122 // Chop the excess comma from the end. 123 size_t len = strnlen(buf, buf_size); 124 if (len > 0 && buf[len - 1] == ',') { 125 buf[len - 1] = 0; 126 } 127 return buf; 128 } 129 130 const char *sc_mount_cmd(char *buf, size_t buf_size, const char *source, const char 131 *target, const char *fs_type, unsigned long mountflags, const 132 void *data) 133 { 134 sc_string_init(buf, buf_size); 135 sc_string_append(buf, buf_size, "mount"); 136 137 // Add filesysystem type if it's there and doesn't have the special value "none" 138 if (fs_type != NULL && strncmp(fs_type, "none", 5) != 0) { 139 sc_string_append(buf, buf_size, " -t "); 140 sc_string_append(buf, buf_size, fs_type); 141 } 142 // Check for some special, dedicated options, that aren't represented with 143 // the generic mount option argument (mount -o ...), by collecting those 144 // options that we will display as command line arguments in 145 // used_special_flags. This is used below to filter out these arguments 146 // from mount_flags when calling sc_mount_opt2str(). 147 int used_special_flags = 0; 148 149 // Bind-ounts (bind) 150 if (mountflags & MS_BIND) { 151 if (mountflags & MS_REC) { 152 sc_string_append(buf, buf_size, " --rbind"); 153 used_special_flags |= MS_REC; 154 } else { 155 sc_string_append(buf, buf_size, " --bind"); 156 } 157 used_special_flags |= MS_BIND; 158 } 159 // Moving mount point location (move) 160 if (mountflags & MS_MOVE) { 161 sc_string_append(buf, buf_size, " --move"); 162 used_special_flags |= MS_MOVE; 163 } 164 // Shared subtree operations (shared, slave, private, unbindable). 165 if (MS_SHARED & mountflags) { 166 if (mountflags & MS_REC) { 167 sc_string_append(buf, buf_size, " --make-rshared"); 168 used_special_flags |= MS_REC; 169 } else { 170 sc_string_append(buf, buf_size, " --make-shared"); 171 } 172 used_special_flags |= MS_SHARED; 173 } 174 175 if (MS_SLAVE & mountflags) { 176 if (mountflags & MS_REC) { 177 sc_string_append(buf, buf_size, " --make-rslave"); 178 used_special_flags |= MS_REC; 179 } else { 180 sc_string_append(buf, buf_size, " --make-slave"); 181 } 182 used_special_flags |= MS_SLAVE; 183 } 184 185 if (MS_PRIVATE & mountflags) { 186 if (mountflags & MS_REC) { 187 sc_string_append(buf, buf_size, " --make-rprivate"); 188 used_special_flags |= MS_REC; 189 } else { 190 sc_string_append(buf, buf_size, " --make-private"); 191 } 192 used_special_flags |= MS_PRIVATE; 193 } 194 195 if (MS_UNBINDABLE & mountflags) { 196 if (mountflags & MS_REC) { 197 sc_string_append(buf, buf_size, " --make-runbindable"); 198 used_special_flags |= MS_REC; 199 } else { 200 sc_string_append(buf, buf_size, " --make-unbindable"); 201 } 202 used_special_flags |= MS_UNBINDABLE; 203 } 204 // If regular option syntax exists then use it. 205 if (mountflags & ~used_special_flags) { 206 char opts_buf[1000] = { 0 }; 207 sc_mount_opt2str(opts_buf, sizeof opts_buf, mountflags & 208 ~used_special_flags); 209 sc_string_append(buf, buf_size, " -o "); 210 sc_string_append(buf, buf_size, opts_buf); 211 } 212 // Add source and target locations 213 if (source != NULL && strncmp(source, "none", 5) != 0) { 214 sc_string_append(buf, buf_size, " "); 215 sc_string_append(buf, buf_size, source); 216 } 217 if (target != NULL && strncmp(target, "none", 5) != 0) { 218 sc_string_append(buf, buf_size, " "); 219 sc_string_append(buf, buf_size, target); 220 } 221 222 return buf; 223 } 224 225 const char *sc_umount_cmd(char *buf, size_t buf_size, const char *target, 226 int flags) 227 { 228 sc_string_init(buf, buf_size); 229 sc_string_append(buf, buf_size, "umount"); 230 231 if (flags & MNT_FORCE) { 232 sc_string_append(buf, buf_size, " --force"); 233 } 234 235 if (flags & MNT_DETACH) { 236 sc_string_append(buf, buf_size, " --lazy"); 237 } 238 if (flags & MNT_EXPIRE) { 239 // NOTE: there's no real command line option for MNT_EXPIRE 240 sc_string_append(buf, buf_size, " --expire"); 241 } 242 if (flags & UMOUNT_NOFOLLOW) { 243 // NOTE: there's no real command line option for UMOUNT_NOFOLLOW 244 sc_string_append(buf, buf_size, " --no-follow"); 245 } 246 if (target != NULL) { 247 sc_string_append(buf, buf_size, " "); 248 sc_string_append(buf, buf_size, target); 249 } 250 251 return buf; 252 } 253 254 #ifndef SNAP_CONFINE_DEBUG_BUILD 255 static const char *use_debug_build = 256 "(disabled) use debug build to see details"; 257 #endif 258 259 static bool sc_do_mount_ex(const char *source, const char *target, 260 const char *fs_type, 261 unsigned long mountflags, const void *data, 262 bool optional) 263 { 264 char buf[10000] = { 0 }; 265 const char *mount_cmd = NULL; 266 267 if (sc_is_debug_enabled()) { 268 #ifdef SNAP_CONFINE_DEBUG_BUILD 269 mount_cmd = sc_mount_cmd(buf, sizeof(buf), source, 270 target, fs_type, mountflags, data); 271 #else 272 mount_cmd = use_debug_build; 273 #endif 274 debug("performing operation: %s", mount_cmd); 275 } 276 if (sc_faulty("mount", NULL) 277 || mount(source, target, fs_type, mountflags, data) < 0) { 278 int saved_errno = errno; 279 if (optional && saved_errno == ENOENT) { 280 // The special-cased value that is allowed to fail. 281 return false; 282 } 283 // Drop privileges so that we can compute our nice error message 284 // without risking an attack on one of the string functions there. 285 sc_privs_drop(); 286 287 // Compute the equivalent mount command. 288 mount_cmd = sc_mount_cmd(buf, sizeof(buf), source, 289 target, fs_type, mountflags, data); 290 // Restore errno and die. 291 errno = saved_errno; 292 die("cannot perform operation: %s", mount_cmd); 293 } 294 return true; 295 } 296 297 void sc_do_mount(const char *source, const char *target, 298 const char *fs_type, unsigned long mountflags, 299 const void *data) 300 { 301 (void)sc_do_mount_ex(source, target, fs_type, mountflags, data, false); 302 } 303 304 bool sc_do_optional_mount(const char *source, const char *target, 305 const char *fs_type, unsigned long mountflags, 306 const void *data) 307 { 308 return sc_do_mount_ex(source, target, fs_type, mountflags, data, true); 309 } 310 311 void sc_do_umount(const char *target, int flags) 312 { 313 char buf[10000] = { 0 }; 314 const char *umount_cmd = NULL; 315 316 if (sc_is_debug_enabled()) { 317 #ifdef SNAP_CONFINE_DEBUG_BUILD 318 umount_cmd = sc_umount_cmd(buf, sizeof(buf), target, flags); 319 #else 320 umount_cmd = use_debug_build; 321 #endif 322 debug("performing operation: %s", umount_cmd); 323 } 324 if (sc_faulty("umount", NULL) || umount2(target, flags) < 0) { 325 // Save errno as ensure can clobber it. 326 int saved_errno = errno; 327 328 // Drop privileges so that we can compute our nice error message 329 // without risking an attack on one of the string functions there. 330 sc_privs_drop(); 331 332 // Compute the equivalent umount command. 333 umount_cmd = sc_umount_cmd(buf, sizeof(buf), target, flags); 334 // Restore errno and die. 335 errno = saved_errno; 336 die("cannot perform operation: %s", umount_cmd); 337 } 338 }