File Index
Files and directories
Sign in
FILE

file_handle_test.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/namespaces/file_handle_test.c C
   1// SPDX-License-Identifier: GPL-2.0
   2#define _GNU_SOURCE
   3#include <errno.h>
   4#include <fcntl.h>
   5#include <grp.h>
   6#include <limits.h>
   7#include <sched.h>
   8#include <stdio.h>
   9#include <stdlib.h>
  10#include <string.h>
  11#include <sys/mount.h>
  12#include <sys/stat.h>
  13#include <sys/types.h>
  14#include <sys/wait.h>
  15#include <unistd.h>
  16#include <linux/unistd.h>
  17#include "kselftest_harness.h"
  18
  19#ifndef FD_NSFS_ROOT
  20#define FD_NSFS_ROOT -10003 /* Root of the nsfs filesystem */
  21#endif
  22
  23TEST(nsfs_net_handle)
  24{
  25	struct file_handle *handle;
  26	int mount_id;
  27	int ret;
  28	int fd;
  29	int ns_fd;
  30	struct stat st1, st2;
  31
  32	/* Drop to unprivileged uid/gid */
  33	ASSERT_EQ(setresgid(65534, 65534, 65534), 0); /* nogroup */
  34	ASSERT_EQ(setresuid(65534, 65534, 65534), 0); /* nobody */
  35
  36	handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
  37	ASSERT_NE(handle, NULL);
  38
  39	/* Open a namespace file descriptor */
  40	ns_fd = open("/proc/self/ns/net", O_RDONLY);
  41	ASSERT_GE(ns_fd, 0);
  42
  43	/* Get handle for the namespace */
  44	handle->handle_bytes = MAX_HANDLE_SZ;
  45	ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
  46	if (ret < 0 && errno == EOPNOTSUPP) {
  47		SKIP(free(handle); close(ns_fd);
  48		     return, "nsfs doesn't support file handles");
  49	}
  50	ASSERT_EQ(ret, 0);
  51	ASSERT_GT(handle->handle_bytes, 0);
  52
  53	/* Try to open using FD_NSFS_ROOT as unprivileged user */
  54	fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
  55	if (fd < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {
  56		SKIP(free(handle); close(ns_fd);
  57		     return,
  58			   "open_by_handle_at with FD_NSFS_ROOT not supported");
  59	}
  60	if (fd < 0 && errno == EPERM) {
  61		SKIP(free(handle); close(ns_fd);
  62		     return,
  63			   "Permission denied for unprivileged user (expected)");
  64	}
  65	ASSERT_GE(fd, 0);
  66
  67	/* Verify we opened the correct namespace */
  68	ASSERT_EQ(fstat(ns_fd, &st1), 0);
  69	ASSERT_EQ(fstat(fd, &st2), 0);
  70	ASSERT_EQ(st1.st_ino, st2.st_ino);
  71	ASSERT_EQ(st1.st_dev, st2.st_dev);
  72
  73	close(fd);
  74	close(ns_fd);
  75	free(handle);
  76}
  77
  78TEST(nsfs_uts_handle)
  79{
  80	struct file_handle *handle;
  81	int mount_id;
  82	int ret;
  83	int fd;
  84	int ns_fd;
  85	struct stat st1, st2;
  86
  87	/* Drop to unprivileged uid/gid */
  88	ASSERT_EQ(setresgid(65534, 65534, 65534), 0); /* nogroup */
  89	ASSERT_EQ(setresuid(65534, 65534, 65534), 0); /* nobody */
  90
  91	handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
  92	ASSERT_NE(handle, NULL);
  93
  94	/* Open UTS namespace file descriptor */
  95	ns_fd = open("/proc/self/ns/uts", O_RDONLY);
  96	ASSERT_GE(ns_fd, 0);
  97
  98	/* Get handle for the namespace */
  99	handle->handle_bytes = MAX_HANDLE_SZ;
 100	ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
 101	if (ret < 0 && errno == EOPNOTSUPP) {
 102		SKIP(free(handle); close(ns_fd);
 103		     return, "nsfs doesn't support file handles");
 104	}
 105	ASSERT_EQ(ret, 0);
 106	ASSERT_GT(handle->handle_bytes, 0);
 107
 108	/* Try to open using FD_NSFS_ROOT */
 109	fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
 110	if (fd < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {
 111		SKIP(free(handle); close(ns_fd);
 112		     return,
 113			   "open_by_handle_at with FD_NSFS_ROOT not supported");
 114	}
 115	ASSERT_GE(fd, 0);
 116
 117	/* Verify we opened the correct namespace */
 118	ASSERT_EQ(fstat(ns_fd, &st1), 0);
 119	ASSERT_EQ(fstat(fd, &st2), 0);
 120	ASSERT_EQ(st1.st_ino, st2.st_ino);
 121	ASSERT_EQ(st1.st_dev, st2.st_dev);
 122
 123	close(fd);
 124	close(ns_fd);
 125	free(handle);
 126}
 127
 128TEST(nsfs_ipc_handle)
 129{
 130	struct file_handle *handle;
 131	int mount_id;
 132	int ret;
 133	int fd;
 134	int ns_fd;
 135	struct stat st1, st2;
 136
 137	/* Drop to unprivileged uid/gid */
 138	ASSERT_EQ(setresgid(65534, 65534, 65534), 0); /* nogroup */
 139	ASSERT_EQ(setresuid(65534, 65534, 65534), 0); /* nobody */
 140
 141	handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
 142	ASSERT_NE(handle, NULL);
 143
 144	/* Open IPC namespace file descriptor */
 145	ns_fd = open("/proc/self/ns/ipc", O_RDONLY);
 146	ASSERT_GE(ns_fd, 0);
 147
 148	/* Get handle for the namespace */
 149	handle->handle_bytes = MAX_HANDLE_SZ;
 150	ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
 151	if (ret < 0 && errno == EOPNOTSUPP) {
 152		SKIP(free(handle); close(ns_fd);
 153		     return, "nsfs doesn't support file handles");
 154	}
 155	ASSERT_EQ(ret, 0);
 156	ASSERT_GT(handle->handle_bytes, 0);
 157
 158	/* Try to open using FD_NSFS_ROOT */
 159	fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
 160	if (fd < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {
 161		SKIP(free(handle); close(ns_fd);
 162		     return,
 163			   "open_by_handle_at with FD_NSFS_ROOT not supported");
 164	}
 165	ASSERT_GE(fd, 0);
 166
 167	/* Verify we opened the correct namespace */
 168	ASSERT_EQ(fstat(ns_fd, &st1), 0);
 169	ASSERT_EQ(fstat(fd, &st2), 0);
 170	ASSERT_EQ(st1.st_ino, st2.st_ino);
 171	ASSERT_EQ(st1.st_dev, st2.st_dev);
 172
 173	close(fd);
 174	close(ns_fd);
 175	free(handle);
 176}
 177
 178TEST(nsfs_pid_handle)
 179{
 180	struct file_handle *handle;
 181	int mount_id;
 182	int ret;
 183	int fd;
 184	int ns_fd;
 185	struct stat st1, st2;
 186
 187	/* Drop to unprivileged uid/gid */
 188	ASSERT_EQ(setresgid(65534, 65534, 65534), 0); /* nogroup */
 189	ASSERT_EQ(setresuid(65534, 65534, 65534), 0); /* nobody */
 190
 191	handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
 192	ASSERT_NE(handle, NULL);
 193
 194	/* Open PID namespace file descriptor */
 195	ns_fd = open("/proc/self/ns/pid", O_RDONLY);
 196	ASSERT_GE(ns_fd, 0);
 197
 198	/* Get handle for the namespace */
 199	handle->handle_bytes = MAX_HANDLE_SZ;
 200	ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
 201	if (ret < 0 && errno == EOPNOTSUPP) {
 202		SKIP(free(handle); close(ns_fd);
 203		     return, "nsfs doesn't support file handles");
 204	}
 205	ASSERT_EQ(ret, 0);
 206	ASSERT_GT(handle->handle_bytes, 0);
 207
 208	/* Try to open using FD_NSFS_ROOT */
 209	fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
 210	if (fd < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {
 211		SKIP(free(handle); close(ns_fd);
 212		     return,
 213			   "open_by_handle_at with FD_NSFS_ROOT not supported");
 214	}
 215	ASSERT_GE(fd, 0);
 216
 217	/* Verify we opened the correct namespace */
 218	ASSERT_EQ(fstat(ns_fd, &st1), 0);
 219	ASSERT_EQ(fstat(fd, &st2), 0);
 220	ASSERT_EQ(st1.st_ino, st2.st_ino);
 221	ASSERT_EQ(st1.st_dev, st2.st_dev);
 222
 223	close(fd);
 224	close(ns_fd);
 225	free(handle);
 226}
 227
 228TEST(nsfs_mnt_handle)
 229{
 230	struct file_handle *handle;
 231	int mount_id;
 232	int ret;
 233	int fd;
 234	int ns_fd;
 235	struct stat st1, st2;
 236
 237	/* Drop to unprivileged uid/gid */
 238	ASSERT_EQ(setresgid(65534, 65534, 65534), 0); /* nogroup */
 239	ASSERT_EQ(setresuid(65534, 65534, 65534), 0); /* nobody */
 240
 241	handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
 242	ASSERT_NE(handle, NULL);
 243
 244	/* Open mount namespace file descriptor */
 245	ns_fd = open("/proc/self/ns/mnt", O_RDONLY);
 246	ASSERT_GE(ns_fd, 0);
 247
 248	/* Get handle for the namespace */
 249	handle->handle_bytes = MAX_HANDLE_SZ;
 250	ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
 251	if (ret < 0 && errno == EOPNOTSUPP) {
 252		SKIP(free(handle); close(ns_fd);
 253		     return, "nsfs doesn't support file handles");
 254	}
 255	ASSERT_EQ(ret, 0);
 256	ASSERT_GT(handle->handle_bytes, 0);
 257
 258	/* Try to open using FD_NSFS_ROOT */
 259	fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
 260	if (fd < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {
 261		SKIP(free(handle); close(ns_fd);
 262		     return,
 263			   "open_by_handle_at with FD_NSFS_ROOT not supported");
 264	}
 265	ASSERT_GE(fd, 0);
 266
 267	/* Verify we opened the correct namespace */
 268	ASSERT_EQ(fstat(ns_fd, &st1), 0);
 269	ASSERT_EQ(fstat(fd, &st2), 0);
 270	ASSERT_EQ(st1.st_ino, st2.st_ino);
 271	ASSERT_EQ(st1.st_dev, st2.st_dev);
 272
 273	close(fd);
 274	close(ns_fd);
 275	free(handle);
 276}
 277
 278TEST(nsfs_user_handle)
 279{
 280	struct file_handle *handle;
 281	int mount_id;
 282	int ret;
 283	int fd;
 284	int ns_fd;
 285	struct stat st1, st2;
 286
 287	/* Drop to unprivileged uid/gid */
 288	ASSERT_EQ(setresgid(65534, 65534, 65534), 0); /* nogroup */
 289	ASSERT_EQ(setresuid(65534, 65534, 65534), 0); /* nobody */
 290
 291	handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
 292	ASSERT_NE(handle, NULL);
 293
 294	/* Open user namespace file descriptor */
 295	ns_fd = open("/proc/self/ns/user", O_RDONLY);
 296	ASSERT_GE(ns_fd, 0);
 297
 298	/* Get handle for the namespace */
 299	handle->handle_bytes = MAX_HANDLE_SZ;
 300	ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
 301	if (ret < 0 && errno == EOPNOTSUPP) {
 302		SKIP(free(handle); close(ns_fd);
 303		     return, "nsfs doesn't support file handles");
 304	}
 305	ASSERT_EQ(ret, 0);
 306	ASSERT_GT(handle->handle_bytes, 0);
 307
 308	/* Try to open using FD_NSFS_ROOT */
 309	fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
 310	if (fd < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {
 311		SKIP(free(handle); close(ns_fd);
 312		     return,
 313			   "open_by_handle_at with FD_NSFS_ROOT not supported");
 314	}
 315	ASSERT_GE(fd, 0);
 316
 317	/* Verify we opened the correct namespace */
 318	ASSERT_EQ(fstat(ns_fd, &st1), 0);
 319	ASSERT_EQ(fstat(fd, &st2), 0);
 320	ASSERT_EQ(st1.st_ino, st2.st_ino);
 321	ASSERT_EQ(st1.st_dev, st2.st_dev);
 322
 323	close(fd);
 324	close(ns_fd);
 325	free(handle);
 326}
 327
 328TEST(nsfs_cgroup_handle)
 329{
 330	struct file_handle *handle;
 331	int mount_id;
 332	int ret;
 333	int fd;
 334	int ns_fd;
 335	struct stat st1, st2;
 336
 337	/* Drop to unprivileged uid/gid */
 338	ASSERT_EQ(setresgid(65534, 65534, 65534), 0); /* nogroup */
 339	ASSERT_EQ(setresuid(65534, 65534, 65534), 0); /* nobody */
 340
 341	handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
 342	ASSERT_NE(handle, NULL);
 343
 344	/* Open cgroup namespace file descriptor */
 345	ns_fd = open("/proc/self/ns/cgroup", O_RDONLY);
 346	if (ns_fd < 0) {
 347		SKIP(free(handle); return, "cgroup namespace not available");
 348	}
 349
 350	/* Get handle for the namespace */
 351	handle->handle_bytes = MAX_HANDLE_SZ;
 352	ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
 353	if (ret < 0 && errno == EOPNOTSUPP) {
 354		SKIP(free(handle); close(ns_fd);
 355		     return, "nsfs doesn't support file handles");
 356	}
 357	ASSERT_EQ(ret, 0);
 358	ASSERT_GT(handle->handle_bytes, 0);
 359
 360	/* Try to open using FD_NSFS_ROOT */
 361	fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
 362	if (fd < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {
 363		SKIP(free(handle); close(ns_fd);
 364		     return,
 365			   "open_by_handle_at with FD_NSFS_ROOT not supported");
 366	}
 367	ASSERT_GE(fd, 0);
 368
 369	/* Verify we opened the correct namespace */
 370	ASSERT_EQ(fstat(ns_fd, &st1), 0);
 371	ASSERT_EQ(fstat(fd, &st2), 0);
 372	ASSERT_EQ(st1.st_ino, st2.st_ino);
 373	ASSERT_EQ(st1.st_dev, st2.st_dev);
 374
 375	close(fd);
 376	close(ns_fd);
 377	free(handle);
 378}
 379
 380TEST(nsfs_time_handle)
 381{
 382	struct file_handle *handle;
 383	int mount_id;
 384	int ret;
 385	int fd;
 386	int ns_fd;
 387	struct stat st1, st2;
 388
 389	/* Drop to unprivileged uid/gid */
 390	ASSERT_EQ(setresgid(65534, 65534, 65534), 0); /* nogroup */
 391	ASSERT_EQ(setresuid(65534, 65534, 65534), 0); /* nobody */
 392
 393	handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
 394	ASSERT_NE(handle, NULL);
 395
 396	/* Open time namespace file descriptor */
 397	ns_fd = open("/proc/self/ns/time", O_RDONLY);
 398	if (ns_fd < 0) {
 399		SKIP(free(handle); return, "time namespace not available");
 400	}
 401
 402	/* Get handle for the namespace */
 403	handle->handle_bytes = MAX_HANDLE_SZ;
 404	ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
 405	if (ret < 0 && errno == EOPNOTSUPP) {
 406		SKIP(free(handle); close(ns_fd);
 407		     return, "nsfs doesn't support file handles");
 408	}
 409	ASSERT_EQ(ret, 0);
 410	ASSERT_GT(handle->handle_bytes, 0);
 411
 412	/* Try to open using FD_NSFS_ROOT */
 413	fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
 414	if (fd < 0 && (errno == EINVAL || errno == EOPNOTSUPP)) {
 415		SKIP(free(handle); close(ns_fd);
 416		     return,
 417			   "open_by_handle_at with FD_NSFS_ROOT not supported");
 418	}
 419	ASSERT_GE(fd, 0);
 420
 421	/* Verify we opened the correct namespace */
 422	ASSERT_EQ(fstat(ns_fd, &st1), 0);
 423	ASSERT_EQ(fstat(fd, &st2), 0);
 424	ASSERT_EQ(st1.st_ino, st2.st_ino);
 425	ASSERT_EQ(st1.st_dev, st2.st_dev);
 426
 427	close(fd);
 428	close(ns_fd);
 429	free(handle);
 430}
 431
 432TEST(nsfs_user_net_namespace_isolation)
 433{
 434	struct file_handle *handle;
 435	int mount_id;
 436	int ret;
 437	int fd;
 438	int ns_fd;
 439	pid_t pid;
 440	int status;
 441	int pipefd[2];
 442	char result;
 443
 444	handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
 445	ASSERT_NE(handle, NULL);
 446
 447	/* Create pipe for communication */
 448	ASSERT_EQ(pipe(pipefd), 0);
 449
 450	/* Get handle for current network namespace */
 451	ns_fd = open("/proc/self/ns/net", O_RDONLY);
 452	ASSERT_GE(ns_fd, 0);
 453
 454	handle->handle_bytes = MAX_HANDLE_SZ;
 455	ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
 456	if (ret < 0 && errno == EOPNOTSUPP) {
 457		SKIP(free(handle); close(ns_fd); close(pipefd[0]);
 458		     close(pipefd[1]);
 459		     return, "nsfs doesn't support file handles");
 460	}
 461	ASSERT_EQ(ret, 0);
 462	close(ns_fd);
 463
 464	pid = fork();
 465	ASSERT_GE(pid, 0);
 466
 467	if (pid == 0) {
 468		/* Child process */
 469		close(pipefd[0]);
 470
 471		/* First create new user namespace to drop privileges */
 472		ret = unshare(CLONE_NEWUSER);
 473		if (ret < 0) {
 474			write(pipefd[1], "U",
 475			      1); /* Unable to create user namespace */
 476			close(pipefd[1]);
 477			exit(0);
 478		}
 479
 480		/* Write uid/gid mappings to maintain some capabilities */
 481		int uid_map_fd = open("/proc/self/uid_map", O_WRONLY);
 482		int gid_map_fd = open("/proc/self/gid_map", O_WRONLY);
 483		int setgroups_fd = open("/proc/self/setgroups", O_WRONLY);
 484
 485		if (uid_map_fd < 0 || gid_map_fd < 0 || setgroups_fd < 0) {
 486			write(pipefd[1], "M", 1); /* Unable to set mappings */
 487			close(pipefd[1]);
 488			exit(0);
 489		}
 490
 491		/* Disable setgroups to allow gid mapping */
 492		write(setgroups_fd, "deny", 4);
 493		close(setgroups_fd);
 494
 495		/* Map current uid/gid to root in the new namespace */
 496		char mapping[64];
 497		snprintf(mapping, sizeof(mapping), "0 %d 1", getuid());
 498		write(uid_map_fd, mapping, strlen(mapping));
 499		close(uid_map_fd);
 500
 501		snprintf(mapping, sizeof(mapping), "0 %d 1", getgid());
 502		write(gid_map_fd, mapping, strlen(mapping));
 503		close(gid_map_fd);
 504
 505		/* Now create new network namespace */
 506		ret = unshare(CLONE_NEWNET);
 507		if (ret < 0) {
 508			write(pipefd[1], "N",
 509			      1); /* Unable to create network namespace */
 510			close(pipefd[1]);
 511			exit(0);
 512		}
 513
 514		/* Try to open parent's network namespace handle from new user+net namespace */
 515		fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
 516
 517		if (fd >= 0) {
 518			/* Should NOT succeed - we're in a different user namespace */
 519			write(pipefd[1], "S", 1); /* Unexpected success */
 520			close(fd);
 521		} else if (errno == ESTALE) {
 522			/* Expected: Stale file handle */
 523			write(pipefd[1], "P", 1);
 524		} else {
 525			/* Other error */
 526			write(pipefd[1], "F", 1);
 527		}
 528
 529		close(pipefd[1]);
 530		exit(0);
 531	}
 532
 533	/* Parent process */
 534	close(pipefd[1]);
 535	ASSERT_EQ(read(pipefd[0], &result, 1), 1);
 536
 537	waitpid(pid, &status, 0);
 538	ASSERT_TRUE(WIFEXITED(status));
 539	ASSERT_EQ(WEXITSTATUS(status), 0);
 540
 541	if (result == 'U') {
 542		SKIP(free(handle); close(pipefd[0]);
 543		     return, "Cannot create new user namespace");
 544	}
 545	if (result == 'M') {
 546		SKIP(free(handle); close(pipefd[0]);
 547		     return, "Cannot set uid/gid mappings");
 548	}
 549	if (result == 'N') {
 550		SKIP(free(handle); close(pipefd[0]);
 551		     return, "Cannot create new network namespace");
 552	}
 553
 554	/* Should fail with permission denied since we're in a different user namespace */
 555	ASSERT_EQ(result, 'P');
 556
 557	close(pipefd[0]);
 558	free(handle);
 559}
 560
 561TEST(nsfs_user_uts_namespace_isolation)
 562{
 563	struct file_handle *handle;
 564	int mount_id;
 565	int ret;
 566	int fd;
 567	int ns_fd;
 568	pid_t pid;
 569	int status;
 570	int pipefd[2];
 571	char result;
 572
 573	handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
 574	ASSERT_NE(handle, NULL);
 575
 576	/* Create pipe for communication */
 577	ASSERT_EQ(pipe(pipefd), 0);
 578
 579	/* Get handle for current UTS namespace */
 580	ns_fd = open("/proc/self/ns/uts", O_RDONLY);
 581	ASSERT_GE(ns_fd, 0);
 582
 583	handle->handle_bytes = MAX_HANDLE_SZ;
 584	ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
 585	if (ret < 0 && errno == EOPNOTSUPP) {
 586		SKIP(free(handle); close(ns_fd); close(pipefd[0]);
 587		     close(pipefd[1]);
 588		     return, "nsfs doesn't support file handles");
 589	}
 590	ASSERT_EQ(ret, 0);
 591	close(ns_fd);
 592
 593	pid = fork();
 594	ASSERT_GE(pid, 0);
 595
 596	if (pid == 0) {
 597		/* Child process */
 598		close(pipefd[0]);
 599
 600		/* First create new user namespace to drop privileges */
 601		ret = unshare(CLONE_NEWUSER);
 602		if (ret < 0) {
 603			write(pipefd[1], "U",
 604			      1); /* Unable to create user namespace */
 605			close(pipefd[1]);
 606			exit(0);
 607		}
 608
 609		/* Write uid/gid mappings to maintain some capabilities */
 610		int uid_map_fd = open("/proc/self/uid_map", O_WRONLY);
 611		int gid_map_fd = open("/proc/self/gid_map", O_WRONLY);
 612		int setgroups_fd = open("/proc/self/setgroups", O_WRONLY);
 613
 614		if (uid_map_fd < 0 || gid_map_fd < 0 || setgroups_fd < 0) {
 615			write(pipefd[1], "M", 1); /* Unable to set mappings */
 616			close(pipefd[1]);
 617			exit(0);
 618		}
 619
 620		/* Disable setgroups to allow gid mapping */
 621		write(setgroups_fd, "deny", 4);
 622		close(setgroups_fd);
 623
 624		/* Map current uid/gid to root in the new namespace */
 625		char mapping[64];
 626		snprintf(mapping, sizeof(mapping), "0 %d 1", getuid());
 627		write(uid_map_fd, mapping, strlen(mapping));
 628		close(uid_map_fd);
 629
 630		snprintf(mapping, sizeof(mapping), "0 %d 1", getgid());
 631		write(gid_map_fd, mapping, strlen(mapping));
 632		close(gid_map_fd);
 633
 634		/* Now create new UTS namespace */
 635		ret = unshare(CLONE_NEWUTS);
 636		if (ret < 0) {
 637			write(pipefd[1], "N",
 638			      1); /* Unable to create UTS namespace */
 639			close(pipefd[1]);
 640			exit(0);
 641		}
 642
 643		/* Try to open parent's UTS namespace handle from new user+uts namespace */
 644		fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
 645
 646		if (fd >= 0) {
 647			/* Should NOT succeed - we're in a different user namespace */
 648			write(pipefd[1], "S", 1); /* Unexpected success */
 649			close(fd);
 650		} else if (errno == ESTALE) {
 651			/* Expected: Stale file handle */
 652			write(pipefd[1], "P", 1);
 653		} else {
 654			/* Other error */
 655			write(pipefd[1], "F", 1);
 656		}
 657
 658		close(pipefd[1]);
 659		exit(0);
 660	}
 661
 662	/* Parent process */
 663	close(pipefd[1]);
 664	ASSERT_EQ(read(pipefd[0], &result, 1), 1);
 665
 666	waitpid(pid, &status, 0);
 667	ASSERT_TRUE(WIFEXITED(status));
 668	ASSERT_EQ(WEXITSTATUS(status), 0);
 669
 670	if (result == 'U') {
 671		SKIP(free(handle); close(pipefd[0]);
 672		     return, "Cannot create new user namespace");
 673	}
 674	if (result == 'M') {
 675		SKIP(free(handle); close(pipefd[0]);
 676		     return, "Cannot set uid/gid mappings");
 677	}
 678	if (result == 'N') {
 679		SKIP(free(handle); close(pipefd[0]);
 680		     return, "Cannot create new UTS namespace");
 681	}
 682
 683	/* Should fail with ESTALE since we're in a different user namespace */
 684	ASSERT_EQ(result, 'P');
 685
 686	close(pipefd[0]);
 687	free(handle);
 688}
 689
 690TEST(nsfs_user_ipc_namespace_isolation)
 691{
 692	struct file_handle *handle;
 693	int mount_id;
 694	int ret;
 695	int fd;
 696	int ns_fd;
 697	pid_t pid;
 698	int status;
 699	int pipefd[2];
 700	char result;
 701
 702	handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
 703	ASSERT_NE(handle, NULL);
 704
 705	/* Create pipe for communication */
 706	ASSERT_EQ(pipe(pipefd), 0);
 707
 708	/* Get handle for current IPC namespace */
 709	ns_fd = open("/proc/self/ns/ipc", O_RDONLY);
 710	ASSERT_GE(ns_fd, 0);
 711
 712	handle->handle_bytes = MAX_HANDLE_SZ;
 713	ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
 714	if (ret < 0 && errno == EOPNOTSUPP) {
 715		SKIP(free(handle); close(ns_fd); close(pipefd[0]);
 716		     close(pipefd[1]);
 717		     return, "nsfs doesn't support file handles");
 718	}
 719	ASSERT_EQ(ret, 0);
 720	close(ns_fd);
 721
 722	pid = fork();
 723	ASSERT_GE(pid, 0);
 724
 725	if (pid == 0) {
 726		/* Child process */
 727		close(pipefd[0]);
 728
 729		/* First create new user namespace to drop privileges */
 730		ret = unshare(CLONE_NEWUSER);
 731		if (ret < 0) {
 732			write(pipefd[1], "U",
 733			      1); /* Unable to create user namespace */
 734			close(pipefd[1]);
 735			exit(0);
 736		}
 737
 738		/* Write uid/gid mappings to maintain some capabilities */
 739		int uid_map_fd = open("/proc/self/uid_map", O_WRONLY);
 740		int gid_map_fd = open("/proc/self/gid_map", O_WRONLY);
 741		int setgroups_fd = open("/proc/self/setgroups", O_WRONLY);
 742
 743		if (uid_map_fd < 0 || gid_map_fd < 0 || setgroups_fd < 0) {
 744			write(pipefd[1], "M", 1); /* Unable to set mappings */
 745			close(pipefd[1]);
 746			exit(0);
 747		}
 748
 749		/* Disable setgroups to allow gid mapping */
 750		write(setgroups_fd, "deny", 4);
 751		close(setgroups_fd);
 752
 753		/* Map current uid/gid to root in the new namespace */
 754		char mapping[64];
 755		snprintf(mapping, sizeof(mapping), "0 %d 1", getuid());
 756		write(uid_map_fd, mapping, strlen(mapping));
 757		close(uid_map_fd);
 758
 759		snprintf(mapping, sizeof(mapping), "0 %d 1", getgid());
 760		write(gid_map_fd, mapping, strlen(mapping));
 761		close(gid_map_fd);
 762
 763		/* Now create new IPC namespace */
 764		ret = unshare(CLONE_NEWIPC);
 765		if (ret < 0) {
 766			write(pipefd[1], "N",
 767			      1); /* Unable to create IPC namespace */
 768			close(pipefd[1]);
 769			exit(0);
 770		}
 771
 772		/* Try to open parent's IPC namespace handle from new user+ipc namespace */
 773		fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
 774
 775		if (fd >= 0) {
 776			/* Should NOT succeed - we're in a different user namespace */
 777			write(pipefd[1], "S", 1); /* Unexpected success */
 778			close(fd);
 779		} else if (errno == ESTALE) {
 780			/* Expected: Stale file handle */
 781			write(pipefd[1], "P", 1);
 782		} else {
 783			/* Other error */
 784			write(pipefd[1], "F", 1);
 785		}
 786
 787		close(pipefd[1]);
 788		exit(0);
 789	}
 790
 791	/* Parent process */
 792	close(pipefd[1]);
 793	ASSERT_EQ(read(pipefd[0], &result, 1), 1);
 794
 795	waitpid(pid, &status, 0);
 796	ASSERT_TRUE(WIFEXITED(status));
 797	ASSERT_EQ(WEXITSTATUS(status), 0);
 798
 799	if (result == 'U') {
 800		SKIP(free(handle); close(pipefd[0]);
 801		     return, "Cannot create new user namespace");
 802	}
 803	if (result == 'M') {
 804		SKIP(free(handle); close(pipefd[0]);
 805		     return, "Cannot set uid/gid mappings");
 806	}
 807	if (result == 'N') {
 808		SKIP(free(handle); close(pipefd[0]);
 809		     return, "Cannot create new IPC namespace");
 810	}
 811
 812	/* Should fail with ESTALE since we're in a different user namespace */
 813	ASSERT_EQ(result, 'P');
 814
 815	close(pipefd[0]);
 816	free(handle);
 817}
 818
 819TEST(nsfs_user_mnt_namespace_isolation)
 820{
 821	struct file_handle *handle;
 822	int mount_id;
 823	int ret;
 824	int fd;
 825	int ns_fd;
 826	pid_t pid;
 827	int status;
 828	int pipefd[2];
 829	char result;
 830
 831	handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
 832	ASSERT_NE(handle, NULL);
 833
 834	/* Create pipe for communication */
 835	ASSERT_EQ(pipe(pipefd), 0);
 836
 837	/* Get handle for current mount namespace */
 838	ns_fd = open("/proc/self/ns/mnt", O_RDONLY);
 839	ASSERT_GE(ns_fd, 0);
 840
 841	handle->handle_bytes = MAX_HANDLE_SZ;
 842	ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
 843	if (ret < 0 && errno == EOPNOTSUPP) {
 844		SKIP(free(handle); close(ns_fd); close(pipefd[0]);
 845		     close(pipefd[1]);
 846		     return, "nsfs doesn't support file handles");
 847	}
 848	ASSERT_EQ(ret, 0);
 849	close(ns_fd);
 850
 851	pid = fork();
 852	ASSERT_GE(pid, 0);
 853
 854	if (pid == 0) {
 855		/* Child process */
 856		close(pipefd[0]);
 857
 858		/* First create new user namespace to drop privileges */
 859		ret = unshare(CLONE_NEWUSER);
 860		if (ret < 0) {
 861			write(pipefd[1], "U",
 862			      1); /* Unable to create user namespace */
 863			close(pipefd[1]);
 864			exit(0);
 865		}
 866
 867		/* Write uid/gid mappings to maintain some capabilities */
 868		int uid_map_fd = open("/proc/self/uid_map", O_WRONLY);
 869		int gid_map_fd = open("/proc/self/gid_map", O_WRONLY);
 870		int setgroups_fd = open("/proc/self/setgroups", O_WRONLY);
 871
 872		if (uid_map_fd < 0 || gid_map_fd < 0 || setgroups_fd < 0) {
 873			write(pipefd[1], "M", 1); /* Unable to set mappings */
 874			close(pipefd[1]);
 875			exit(0);
 876		}
 877
 878		/* Disable setgroups to allow gid mapping */
 879		write(setgroups_fd, "deny", 4);
 880		close(setgroups_fd);
 881
 882		/* Map current uid/gid to root in the new namespace */
 883		char mapping[64];
 884		snprintf(mapping, sizeof(mapping), "0 %d 1", getuid());
 885		write(uid_map_fd, mapping, strlen(mapping));
 886		close(uid_map_fd);
 887
 888		snprintf(mapping, sizeof(mapping), "0 %d 1", getgid());
 889		write(gid_map_fd, mapping, strlen(mapping));
 890		close(gid_map_fd);
 891
 892		/* Now create new mount namespace */
 893		ret = unshare(CLONE_NEWNS);
 894		if (ret < 0) {
 895			write(pipefd[1], "N",
 896			      1); /* Unable to create mount namespace */
 897			close(pipefd[1]);
 898			exit(0);
 899		}
 900
 901		/* Try to open parent's mount namespace handle from new user+mnt namespace */
 902		fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
 903
 904		if (fd >= 0) {
 905			/* Should NOT succeed - we're in a different user namespace */
 906			write(pipefd[1], "S", 1); /* Unexpected success */
 907			close(fd);
 908		} else if (errno == ESTALE) {
 909			/* Expected: Stale file handle */
 910			write(pipefd[1], "P", 1);
 911		} else {
 912			/* Other error */
 913			write(pipefd[1], "F", 1);
 914		}
 915
 916		close(pipefd[1]);
 917		exit(0);
 918	}
 919
 920	/* Parent process */
 921	close(pipefd[1]);
 922	ASSERT_EQ(read(pipefd[0], &result, 1), 1);
 923
 924	waitpid(pid, &status, 0);
 925	ASSERT_TRUE(WIFEXITED(status));
 926	ASSERT_EQ(WEXITSTATUS(status), 0);
 927
 928	if (result == 'U') {
 929		SKIP(free(handle); close(pipefd[0]);
 930		     return, "Cannot create new user namespace");
 931	}
 932	if (result == 'M') {
 933		SKIP(free(handle); close(pipefd[0]);
 934		     return, "Cannot set uid/gid mappings");
 935	}
 936	if (result == 'N') {
 937		SKIP(free(handle); close(pipefd[0]);
 938		     return, "Cannot create new mount namespace");
 939	}
 940
 941	/* Should fail with ESTALE since we're in a different user namespace */
 942	ASSERT_EQ(result, 'P');
 943
 944	close(pipefd[0]);
 945	free(handle);
 946}
 947
 948TEST(nsfs_user_cgroup_namespace_isolation)
 949{
 950	struct file_handle *handle;
 951	int mount_id;
 952	int ret;
 953	int fd;
 954	int ns_fd;
 955	pid_t pid;
 956	int status;
 957	int pipefd[2];
 958	char result;
 959
 960	handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
 961	ASSERT_NE(handle, NULL);
 962
 963	/* Create pipe for communication */
 964	ASSERT_EQ(pipe(pipefd), 0);
 965
 966	/* Get handle for current cgroup namespace */
 967	ns_fd = open("/proc/self/ns/cgroup", O_RDONLY);
 968	if (ns_fd < 0) {
 969		SKIP(free(handle); close(pipefd[0]); close(pipefd[1]);
 970		     return, "cgroup namespace not available");
 971	}
 972
 973	handle->handle_bytes = MAX_HANDLE_SZ;
 974	ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
 975	if (ret < 0 && errno == EOPNOTSUPP) {
 976		SKIP(free(handle); close(ns_fd); close(pipefd[0]);
 977		     close(pipefd[1]);
 978		     return, "nsfs doesn't support file handles");
 979	}
 980	ASSERT_EQ(ret, 0);
 981	close(ns_fd);
 982
 983	pid = fork();
 984	ASSERT_GE(pid, 0);
 985
 986	if (pid == 0) {
 987		/* Child process */
 988		close(pipefd[0]);
 989
 990		/* First create new user namespace to drop privileges */
 991		ret = unshare(CLONE_NEWUSER);
 992		if (ret < 0) {
 993			write(pipefd[1], "U",
 994			      1); /* Unable to create user namespace */
 995			close(pipefd[1]);
 996			exit(0);
 997		}
 998
 999		/* Write uid/gid mappings to maintain some capabilities */
1000		int uid_map_fd = open("/proc/self/uid_map", O_WRONLY);
1001		int gid_map_fd = open("/proc/self/gid_map", O_WRONLY);
1002		int setgroups_fd = open("/proc/self/setgroups", O_WRONLY);
1003
1004		if (uid_map_fd < 0 || gid_map_fd < 0 || setgroups_fd < 0) {
1005			write(pipefd[1], "M", 1); /* Unable to set mappings */
1006			close(pipefd[1]);
1007			exit(0);
1008		}
1009
1010		/* Disable setgroups to allow gid mapping */
1011		write(setgroups_fd, "deny", 4);
1012		close(setgroups_fd);
1013
1014		/* Map current uid/gid to root in the new namespace */
1015		char mapping[64];
1016		snprintf(mapping, sizeof(mapping), "0 %d 1", getuid());
1017		write(uid_map_fd, mapping, strlen(mapping));
1018		close(uid_map_fd);
1019
1020		snprintf(mapping, sizeof(mapping), "0 %d 1", getgid());
1021		write(gid_map_fd, mapping, strlen(mapping));
1022		close(gid_map_fd);
1023
1024		/* Now create new cgroup namespace */
1025		ret = unshare(CLONE_NEWCGROUP);
1026		if (ret < 0) {
1027			write(pipefd[1], "N",
1028			      1); /* Unable to create cgroup namespace */
1029			close(pipefd[1]);
1030			exit(0);
1031		}
1032
1033		/* Try to open parent's cgroup namespace handle from new user+cgroup namespace */
1034		fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
1035
1036		if (fd >= 0) {
1037			/* Should NOT succeed - we're in a different user namespace */
1038			write(pipefd[1], "S", 1); /* Unexpected success */
1039			close(fd);
1040		} else if (errno == ESTALE) {
1041			/* Expected: Stale file handle */
1042			write(pipefd[1], "P", 1);
1043		} else {
1044			/* Other error */
1045			write(pipefd[1], "F", 1);
1046		}
1047
1048		close(pipefd[1]);
1049		exit(0);
1050	}
1051
1052	/* Parent process */
1053	close(pipefd[1]);
1054	ASSERT_EQ(read(pipefd[0], &result, 1), 1);
1055
1056	waitpid(pid, &status, 0);
1057	ASSERT_TRUE(WIFEXITED(status));
1058	ASSERT_EQ(WEXITSTATUS(status), 0);
1059
1060	if (result == 'U') {
1061		SKIP(free(handle); close(pipefd[0]);
1062		     return, "Cannot create new user namespace");
1063	}
1064	if (result == 'M') {
1065		SKIP(free(handle); close(pipefd[0]);
1066		     return, "Cannot set uid/gid mappings");
1067	}
1068	if (result == 'N') {
1069		SKIP(free(handle); close(pipefd[0]);
1070		     return, "Cannot create new cgroup namespace");
1071	}
1072
1073	/* Should fail with ESTALE since we're in a different user namespace */
1074	ASSERT_EQ(result, 'P');
1075
1076	close(pipefd[0]);
1077	free(handle);
1078}
1079
1080TEST(nsfs_user_pid_namespace_isolation)
1081{
1082	struct file_handle *handle;
1083	int mount_id;
1084	int ret;
1085	int fd;
1086	int ns_fd;
1087	pid_t pid;
1088	int status;
1089	int pipefd[2];
1090	char result;
1091
1092	handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
1093	ASSERT_NE(handle, NULL);
1094
1095	/* Create pipe for communication */
1096	ASSERT_EQ(pipe(pipefd), 0);
1097
1098	/* Get handle for current PID namespace */
1099	ns_fd = open("/proc/self/ns/pid", O_RDONLY);
1100	ASSERT_GE(ns_fd, 0);
1101
1102	handle->handle_bytes = MAX_HANDLE_SZ;
1103	ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
1104	if (ret < 0 && errno == EOPNOTSUPP) {
1105		SKIP(free(handle); close(ns_fd); close(pipefd[0]);
1106		     close(pipefd[1]);
1107		     return, "nsfs doesn't support file handles");
1108	}
1109	ASSERT_EQ(ret, 0);
1110	close(ns_fd);
1111
1112	pid = fork();
1113	ASSERT_GE(pid, 0);
1114
1115	if (pid == 0) {
1116		/* Child process */
1117		close(pipefd[0]);
1118
1119		/* First create new user namespace to drop privileges */
1120		ret = unshare(CLONE_NEWUSER);
1121		if (ret < 0) {
1122			write(pipefd[1], "U",
1123			      1); /* Unable to create user namespace */
1124			close(pipefd[1]);
1125			exit(0);
1126		}
1127
1128		/* Write uid/gid mappings to maintain some capabilities */
1129		int uid_map_fd = open("/proc/self/uid_map", O_WRONLY);
1130		int gid_map_fd = open("/proc/self/gid_map", O_WRONLY);
1131		int setgroups_fd = open("/proc/self/setgroups", O_WRONLY);
1132
1133		if (uid_map_fd < 0 || gid_map_fd < 0 || setgroups_fd < 0) {
1134			write(pipefd[1], "M", 1); /* Unable to set mappings */
1135			close(pipefd[1]);
1136			exit(0);
1137		}
1138
1139		/* Disable setgroups to allow gid mapping */
1140		write(setgroups_fd, "deny", 4);
1141		close(setgroups_fd);
1142
1143		/* Map current uid/gid to root in the new namespace */
1144		char mapping[64];
1145		snprintf(mapping, sizeof(mapping), "0 %d 1", getuid());
1146		write(uid_map_fd, mapping, strlen(mapping));
1147		close(uid_map_fd);
1148
1149		snprintf(mapping, sizeof(mapping), "0 %d 1", getgid());
1150		write(gid_map_fd, mapping, strlen(mapping));
1151		close(gid_map_fd);
1152
1153		/* Now create new PID namespace - requires fork to take effect */
1154		ret = unshare(CLONE_NEWPID);
1155		if (ret < 0) {
1156			write(pipefd[1], "N",
1157			      1); /* Unable to create PID namespace */
1158			close(pipefd[1]);
1159			exit(0);
1160		}
1161
1162		/* Fork again for PID namespace to take effect */
1163		pid_t child_pid = fork();
1164		if (child_pid < 0) {
1165			write(pipefd[1], "N",
1166			      1); /* Unable to fork in PID namespace */
1167			close(pipefd[1]);
1168			exit(0);
1169		}
1170
1171		if (child_pid == 0) {
1172			/* Grandchild in new PID namespace */
1173			/* Try to open parent's PID namespace handle from new user+pid namespace */
1174			fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
1175
1176			if (fd >= 0) {
1177				/* Should NOT succeed - we're in a different user namespace */
1178				write(pipefd[1], "S",
1179				      1); /* Unexpected success */
1180				close(fd);
1181			} else if (errno == ESTALE) {
1182				/* Expected: Stale file handle */
1183				write(pipefd[1], "P", 1);
1184			} else {
1185				/* Other error */
1186				write(pipefd[1], "F", 1);
1187			}
1188
1189			close(pipefd[1]);
1190			exit(0);
1191		}
1192
1193		/* Wait for grandchild */
1194		waitpid(child_pid, NULL, 0);
1195		exit(0);
1196	}
1197
1198	/* Parent process */
1199	close(pipefd[1]);
1200	ASSERT_EQ(read(pipefd[0], &result, 1), 1);
1201
1202	waitpid(pid, &status, 0);
1203	ASSERT_TRUE(WIFEXITED(status));
1204	ASSERT_EQ(WEXITSTATUS(status), 0);
1205
1206	if (result == 'U') {
1207		SKIP(free(handle); close(pipefd[0]);
1208		     return, "Cannot create new user namespace");
1209	}
1210	if (result == 'M') {
1211		SKIP(free(handle); close(pipefd[0]);
1212		     return, "Cannot set uid/gid mappings");
1213	}
1214	if (result == 'N') {
1215		SKIP(free(handle); close(pipefd[0]);
1216		     return, "Cannot create new PID namespace");
1217	}
1218
1219	/* Should fail with ESTALE since we're in a different user namespace */
1220	ASSERT_EQ(result, 'P');
1221
1222	close(pipefd[0]);
1223	free(handle);
1224}
1225
1226TEST(nsfs_user_time_namespace_isolation)
1227{
1228	struct file_handle *handle;
1229	int mount_id;
1230	int ret;
1231	int fd;
1232	int ns_fd;
1233	pid_t pid;
1234	int status;
1235	int pipefd[2];
1236	char result;
1237
1238	handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
1239	ASSERT_NE(handle, NULL);
1240
1241	/* Create pipe for communication */
1242	ASSERT_EQ(pipe(pipefd), 0);
1243
1244	/* Get handle for current time namespace */
1245	ns_fd = open("/proc/self/ns/time", O_RDONLY);
1246	if (ns_fd < 0) {
1247		SKIP(free(handle); close(pipefd[0]); close(pipefd[1]);
1248		     return, "time namespace not available");
1249	}
1250
1251	handle->handle_bytes = MAX_HANDLE_SZ;
1252	ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
1253	if (ret < 0 && errno == EOPNOTSUPP) {
1254		SKIP(free(handle); close(ns_fd); close(pipefd[0]);
1255		     close(pipefd[1]);
1256		     return, "nsfs doesn't support file handles");
1257	}
1258	ASSERT_EQ(ret, 0);
1259	close(ns_fd);
1260
1261	pid = fork();
1262	ASSERT_GE(pid, 0);
1263
1264	if (pid == 0) {
1265		/* Child process */
1266		close(pipefd[0]);
1267
1268		/* First create new user namespace to drop privileges */
1269		ret = unshare(CLONE_NEWUSER);
1270		if (ret < 0) {
1271			write(pipefd[1], "U",
1272			      1); /* Unable to create user namespace */
1273			close(pipefd[1]);
1274			exit(0);
1275		}
1276
1277		/* Write uid/gid mappings to maintain some capabilities */
1278		int uid_map_fd = open("/proc/self/uid_map", O_WRONLY);
1279		int gid_map_fd = open("/proc/self/gid_map", O_WRONLY);
1280		int setgroups_fd = open("/proc/self/setgroups", O_WRONLY);
1281
1282		if (uid_map_fd < 0 || gid_map_fd < 0 || setgroups_fd < 0) {
1283			write(pipefd[1], "M", 1); /* Unable to set mappings */
1284			close(pipefd[1]);
1285			exit(0);
1286		}
1287
1288		/* Disable setgroups to allow gid mapping */
1289		write(setgroups_fd, "deny", 4);
1290		close(setgroups_fd);
1291
1292		/* Map current uid/gid to root in the new namespace */
1293		char mapping[64];
1294		snprintf(mapping, sizeof(mapping), "0 %d 1", getuid());
1295		write(uid_map_fd, mapping, strlen(mapping));
1296		close(uid_map_fd);
1297
1298		snprintf(mapping, sizeof(mapping), "0 %d 1", getgid());
1299		write(gid_map_fd, mapping, strlen(mapping));
1300		close(gid_map_fd);
1301
1302		/* Now create new time namespace - requires fork to take effect */
1303		ret = unshare(CLONE_NEWTIME);
1304		if (ret < 0) {
1305			write(pipefd[1], "N",
1306			      1); /* Unable to create time namespace */
1307			close(pipefd[1]);
1308			exit(0);
1309		}
1310
1311		/* Fork again for time namespace to take effect */
1312		pid_t child_pid = fork();
1313		if (child_pid < 0) {
1314			write(pipefd[1], "N",
1315			      1); /* Unable to fork in time namespace */
1316			close(pipefd[1]);
1317			exit(0);
1318		}
1319
1320		if (child_pid == 0) {
1321			/* Grandchild in new time namespace */
1322			/* Try to open parent's time namespace handle from new user+time namespace */
1323			fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDONLY);
1324
1325			if (fd >= 0) {
1326				/* Should NOT succeed - we're in a different user namespace */
1327				write(pipefd[1], "S",
1328				      1); /* Unexpected success */
1329				close(fd);
1330			} else if (errno == ESTALE) {
1331				/* Expected: Stale file handle */
1332				write(pipefd[1], "P", 1);
1333			} else {
1334				/* Other error */
1335				write(pipefd[1], "F", 1);
1336			}
1337
1338			close(pipefd[1]);
1339			exit(0);
1340		}
1341
1342		/* Wait for grandchild */
1343		waitpid(child_pid, NULL, 0);
1344		exit(0);
1345	}
1346
1347	/* Parent process */
1348	close(pipefd[1]);
1349	ASSERT_EQ(read(pipefd[0], &result, 1), 1);
1350
1351	waitpid(pid, &status, 0);
1352	ASSERT_TRUE(WIFEXITED(status));
1353	ASSERT_EQ(WEXITSTATUS(status), 0);
1354
1355	if (result == 'U') {
1356		SKIP(free(handle); close(pipefd[0]);
1357		     return, "Cannot create new user namespace");
1358	}
1359	if (result == 'M') {
1360		SKIP(free(handle); close(pipefd[0]);
1361		     return, "Cannot set uid/gid mappings");
1362	}
1363	if (result == 'N') {
1364		SKIP(free(handle); close(pipefd[0]);
1365		     return, "Cannot create new time namespace");
1366	}
1367
1368	/* Should fail with ESTALE since we're in a different user namespace */
1369	ASSERT_EQ(result, 'P');
1370
1371	close(pipefd[0]);
1372	free(handle);
1373}
1374
1375TEST(nsfs_open_flags)
1376{
1377	struct file_handle *handle;
1378	int mount_id;
1379	int ret;
1380	int fd;
1381	int ns_fd;
1382
1383	handle = malloc(sizeof(*handle) + MAX_HANDLE_SZ);
1384	ASSERT_NE(handle, NULL);
1385
1386	/* Open a namespace file descriptor */
1387	ns_fd = open("/proc/self/ns/net", O_RDONLY);
1388	ASSERT_GE(ns_fd, 0);
1389
1390	/* Get handle for the namespace */
1391	handle->handle_bytes = MAX_HANDLE_SZ;
1392	ret = name_to_handle_at(ns_fd, "", handle, &mount_id, AT_EMPTY_PATH);
1393	if (ret < 0 && errno == EOPNOTSUPP) {
1394		SKIP(free(handle); close(ns_fd);
1395		     return, "nsfs doesn't support file handles");
1396	}
1397	ASSERT_EQ(ret, 0);
1398	ASSERT_GT(handle->handle_bytes, 0);
1399
1400	/* Test invalid flags that should fail */
1401	fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_WRONLY);
1402	ASSERT_LT(fd, 0);
1403	ASSERT_EQ(errno, EPERM);
1404
1405	fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_RDWR);
1406	ASSERT_LT(fd, 0);
1407	ASSERT_EQ(errno, EPERM);
1408
1409	fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_TRUNC);
1410	ASSERT_LT(fd, 0);
1411	ASSERT_EQ(errno, EPERM);
1412
1413	fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_DIRECT);
1414	ASSERT_LT(fd, 0);
1415	ASSERT_EQ(errno, EINVAL);
1416
1417	fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_TMPFILE);
1418	ASSERT_LT(fd, 0);
1419	ASSERT_EQ(errno, EINVAL);
1420
1421	fd = open_by_handle_at(FD_NSFS_ROOT, handle, O_DIRECTORY);
1422	ASSERT_LT(fd, 0);
1423	ASSERT_EQ(errno, ENOTDIR);
1424
1425	close(ns_fd);
1426	free(handle);
1427}
1428
1429TEST_HARNESS_MAIN
~/ Read-only file preview.