11 #include <linux/limits.h>
15 #include <sys/mount.h>
52 const uid_t euid = geteuid();
53 const uid_t egid = getegid();
57 "cannot create root user namespace (" +
StringifyInt(failure) +
" / "
71 if (rvi !=
sizeof(pid_t))
74 if (rvi !=
sizeof(pid_t))
81 const bool rv =
MkdirDeep(path, 0700,
true );
96 if (S_ISDIR(info.st_mode))
97 rv = rmdir(path.c_str());
99 rv = unlink(path.c_str());
101 if (rv == 0 || errno == ENOENT)
110 const std::vector<std::string> &new_paths) {
112 std::vector<std::string> umount_targets;
113 for (
unsigned i = 0; i < all_mounts.size(); ++i) {
114 if (
HasPrefix(all_mounts[i], path +
"/",
false ))
115 umount_targets.push_back(all_mounts[i]);
118 std::sort(umount_targets.begin(), umount_targets.end());
119 std::vector<std::string>::reverse_iterator iter = umount_targets.rbegin();
120 std::vector<std::string>::reverse_iterator iend = umount_targets.rend();
121 for (; iter != iend; ++iter) {
124 if (!rvb && errno != EINVAL && errno != EACCES) {
130 std::vector<std::string> sorted_new_paths(new_paths);
131 std::sort(sorted_new_paths.begin(), sorted_new_paths.end());
132 iter = sorted_new_paths.rbegin();
133 iend = sorted_new_paths.rend();
134 for (; iter != iend; ++iter) {
135 std::string p = *iter;
136 while (p.length() > path.length()) {
142 if (errno != ENOTEMPTY)
156 void CmdEnter::CreateUnderlay(
const std::string &source_dir,
157 const std::string &dest_dir,
158 const std::vector<std::string> &empty_dirs,
159 std::vector<std::string> *new_paths) {
161 source_dir.c_str(), dest_dir.c_str());
164 std::vector<std::string> empty_toplevel_dirs;
165 for (
unsigned i = 0; i < empty_dirs.size(); ++i) {
166 std::string toplevel_dir = empty_dirs[i];
169 empty_toplevel_dirs.push_back(toplevel_dir);
172 const std::string dest_empty_dir = dest_dir + toplevel_dir;
174 dest_empty_dir.c_str());
176 new_paths->push_back(dest_empty_dir);
179 std::vector<std::string> names;
180 std::vector<mode_t> modes;
182 const std::string d = source_dir.empty() ?
"/" : source_dir;
186 throw EPublish(
"cannot list directory " + d);
191 for (
unsigned i = 0; i < names.size(); ++i) {
192 const std::string
source = source_dir +
"/" + names[i] +
"/";
193 const std::string dest = dest_dir +
"/" + names[i] +
"/";
195 iter_swap(names.begin(), names.begin() + i);
196 iter_swap(modes.begin(), modes.begin() + i);
205 for (
unsigned i = 0; i < names.size(); ++i) {
206 if (std::find(empty_toplevel_dirs.begin(), empty_toplevel_dirs.end(),
207 std::string(
"/") + names[i])
208 != empty_toplevel_dirs.end()) {
212 const std::string
source = source_dir +
"/" + names[i];
213 const std::string dest = dest_dir +
"/" + names[i];
214 new_paths->push_back(dest);
215 if (S_ISLNK(modes[i])) {
216 char buf[PATH_MAX + 1];
217 const ssize_t nchars = readlink(source.c_str(), buf, PATH_MAX);
219 throw EPublish(
"cannot read symlink " + source);
223 if (S_ISDIR(modes[i])) {
232 throw EPublish(
"cannot bind mount " + source +
" --> " + dest +
" ("
240 for (
unsigned i = 0; i < empty_toplevel_dirs.size(); ++i) {
241 const std::string toplevel_dir = empty_toplevel_dirs[i];
242 std::vector<std::string> empty_sub_dir;
243 empty_sub_dir.push_back(empty_dirs[i].substr(toplevel_dir.length()));
244 if (!empty_sub_dir[0].empty()) {
245 CreateUnderlay(source_dir + toplevel_dir,
246 dest_dir + toplevel_dir,
253 void CmdEnter::WriteCvmfsConfig(
const std::string &extra_config) {
256 if (!extra_config.empty())
257 options_manager.
ParsePath(extra_config,
false );
259 options_manager.
SetValue(
"CVMFS_MOUNT_DIR",
260 settings_spool_area_.readonly_mnt());
261 options_manager.
SetValue(
"CVMFS_AUTO_UPDATE",
"no");
262 options_manager.
SetValue(
"CVMFS_NFS_SOURCE",
"no");
263 options_manager.
SetValue(
"CVMFS_HIDE_MAGIC_XATTRS",
"yes");
264 options_manager.
SetValue(
"CVMFS_SERVER_CACHE_MODE",
"yes");
265 options_manager.
SetValue(
"CVMFS_CLAIM_OWNERSHIP",
"yes");
266 options_manager.
SetValue(
"CVMFS_USYSLOG", settings_spool_area_.client_log());
267 options_manager.
SetValue(
"CVMFS_RELOAD_SOCKETS",
268 settings_spool_area_.cache_dir());
269 options_manager.
SetValue(
"CVMFS_WORKSPACE", settings_spool_area_.cache_dir());
270 options_manager.
SetValue(
"CVMFS_CACHE_PRIMARY",
"private");
271 options_manager.
SetValue(
"CVMFS_CACHE_private_TYPE",
"posix");
272 options_manager.
SetValue(
"CVMFS_CACHE_private_BASE",
273 settings_spool_area_.cache_dir());
274 options_manager.
SetValue(
"CVMFS_CACHE_private_SHARED",
"on");
275 options_manager.
SetValue(
"CVMFS_CACHE_private_QUOTA_LIMIT",
"4000");
281 throw EPublish(
"cannot write client config to "
282 + settings_spool_area_.client_config());
287 void CmdEnter::MountCvmfs() {
289 throw EPublish(
"cannot find executable " + cvmfs2_binary_,
290 EPublish::kFailMissingDependency);
293 const int fd_stdout = open(stdout_path_.c_str(),
295 const int fd_stderr = open(stderr_path_.c_str(),
298 const std::string cvmfs_config = settings_spool_area_.client_config();
300 std::vector<std::string> cmdline;
301 cmdline.push_back(cvmfs2_binary_);
302 cmdline.push_back(
"-o");
303 cmdline.push_back(
"allow_other,config=" + cvmfs_config);
304 cmdline.push_back(fqrn_);
305 cmdline.push_back(settings_spool_area_.readonly_mnt());
306 std::set<int> preserved_fds;
307 preserved_fds.insert(0);
308 preserved_fds.insert(1);
309 preserved_fds.insert(2);
310 std::map<int, int> map_fds;
311 map_fds[fd_stdout] = 1;
312 map_fds[fd_stderr] = 2;
314 bool rvb =
ManagedExec(cmdline, preserved_fds, map_fds,
320 throw EPublish(
"cannot run " + cvmfs2_binary_);
325 if (exit_code != 0) {
326 throw EPublish(
"cannot mount cvmfs read-only branch ("
331 rvb =
BindMount(settings_spool_area_.readonly_mnt(),
332 rootfs_dir_ + settings_spool_area_.readonly_mnt());
334 throw EPublish(
"cannot map CernVM-FS mount point");
338 void CmdEnter::MountOverlayfs() {
340 throw EPublish(
"cannot find executable " + overlayfs_binary_,
341 EPublish::kFailMissingDependency);
344 const int fd_stdout = open(stdout_path_.c_str(),
346 const int fd_stderr = open(stderr_path_.c_str(),
349 std::vector<std::string> cmdline;
350 cmdline.push_back(overlayfs_binary_);
351 cmdline.push_back(
"-o");
352 cmdline.push_back(
string(
"lowerdir=") + settings_spool_area_.readonly_mnt()
353 +
",upperdir=" + settings_spool_area_.scratch_dir()
354 +
",workdir=" + settings_spool_area_.ovl_work_dir());
355 cmdline.push_back(rootfs_dir_ + settings_spool_area_.union_mnt());
356 std::set<int> preserved_fds;
357 preserved_fds.insert(0);
358 preserved_fds.insert(1);
359 preserved_fds.insert(2);
360 std::map<int, int> map_fds;
361 map_fds[fd_stdout] = 1;
362 map_fds[fd_stderr] = 2;
365 ManagedExec(cmdline, preserved_fds, map_fds,
true ,
366 false ,
false , &pid_child);
370 throw EPublish(
"cannot run " + overlayfs_binary_);
375 if (exit_code != 0) {
376 throw EPublish(
"cannot mount overlay file system ("
383 std::string CmdEnter::GetCvmfsXattr(
const std::string &name) {
386 std::string(
"user.") + name, &xattr);
388 throw EPublish(
"cannot get extrended attribute " + name +
" from "
389 + settings_spool_area_.readonly_mnt());
395 void CmdEnter::CleanupSession(
bool keep_logs,
396 const std::vector<std::string> &new_paths) {
398 "Cleaning out session directory... ");
400 std::string pid_xattr;
404 throw EPublish(
"cannot find CernVM-FS process");
407 const std::string union_mnt = rootfs_dir_ + settings_spool_area_.union_mnt();
410 throw EPublish(
"cannot unmount overlayfs on " + union_mnt);
412 (rootfs_dir_ + settings_spool_area_.readonly_mnt()).c_str());
414 throw EPublish(
"cannot unmount mapped CernVM-FS on " + rootfs_dir_
415 + settings_spool_area_.readonly_mnt());
419 throw EPublish(
"cannot unmount CernVM-FS on "
420 + settings_spool_area_.readonly_mnt());
431 rvb =
RemoveTree(settings_spool_area_.ovl_work_dir());
433 throw EPublish(
"cannot remove " + settings_spool_area_.ovl_work_dir());
434 rvb =
RemoveTree(settings_spool_area_.scratch_base());
436 throw EPublish(
"cannot remove " + settings_spool_area_.scratch_base());
437 rvb =
RemoveTree(settings_spool_area_.cache_dir());
439 throw EPublish(
"cannot remove " + settings_spool_area_.cache_dir());
441 rvb =
RemoveTree(settings_spool_area_.tmp_dir());
443 throw EPublish(
"cannot remove " + settings_spool_area_.tmp_dir());
447 settings_spool_area_.log_dir().c_str());
451 rvb =
RemoveTree(settings_spool_area_.log_dir());
453 RemoveSingle(session_dir_ +
"/" + fqrn_ +
"/server.conf");
466 if (!sanitizer.
IsValid(fqrn_)) {
467 throw EPublish(
"malformed repository name: " + fqrn_);
475 Env::DropCapabilities();
477 if (options.
Has(
"cvmfs2")) {
478 cvmfs2_binary_ = options.
GetString(
"cvmfs2");
481 setenv(
"CVMFS_LIBRARY_PATH",
GetParentPath(cvmfs2_binary_).c_str(), 0);
488 if (session_dir_.empty())
489 throw EPublish(
"cannot create session directory in " + workspace);
490 settings_spool_area_.SetUnionMount(std::string(
"/cvmfs/") + fqrn_);
491 settings_spool_area_.SetSpoolArea(session_dir_);
492 settings_spool_area_.EnsureDirectories();
493 rootfs_dir_ = session_dir_ +
"/rootfs";
495 stdout_path_ = settings_spool_area_.log_dir() +
"/stdout.log";
496 stderr_path_ = settings_spool_area_.log_dir() +
"/stderr.log";
500 const uid_t uid = geteuid();
501 const gid_t gid = getegid();
504 "*** NOTE: This is currently an experimental CernVM-FS feature\n");
506 "Entering ephemeral writable shell for %s... ",
507 settings_spool_area_.union_mnt().c_str());
511 std::vector<std::string> empty_dirs;
512 empty_dirs.push_back(settings_spool_area_.union_mnt());
513 empty_dirs.push_back(
"/proc");
514 std::vector<std::string> new_paths;
515 CreateUnderlay(
"", rootfs_dir_, empty_dirs, &new_paths);
518 rvb =
SymlinkForced(session_dir_, rootfs_dir_ +
"/.cvmfsenter");
520 throw EPublish(
"cannot create marker file " + rootfs_dir_ +
"/.cvmfsenter");
521 new_paths.push_back(rootfs_dir_ +
"/.cvmfsenter");
524 throw EPublish(
"cannot mount " + rootfs_dir_ +
"/proc");
525 setenv(
"CVMFS_ENTER_SESSION_DIR", session_dir_.c_str(), 1 );
528 "Mounting CernVM-FS read-only layer... ");
544 env_conf_ = session_dir_ +
"/env.conf";
545 rvb =
SafeWriteToFile(std::string(
"CVMFS_FQRN=") + fqrn_ +
"\n", env_conf_,
548 throw EPublish(
"cannot create session environment file");
551 "Mounting union file system... ");
558 throw EPublish(
"cannot create subshell");
561 if (!options.
Has(
"root")) {
565 "cannot create user namespace for " +
StringifyInt(uid) +
":"
573 rootfs_dir_.c_str());
574 int rvi = chroot(rootfs_dir_.c_str());
576 throw EPublish(
"cannot chroot to " + rootfs_dir_);
582 if (options.
Has(
"transaction")) {
584 "Starting a transaction inside the enter shell");
586 if (options.
Has(
"repo-config")) {
587 repo_config_ = options.
GetString(
"repo-config");
589 "Parsing the external configuration for the repository at %s",
590 repo_config_.c_str());
593 const std::string config_file =
594 repo_config_ +
"/" + fqrn_ +
"/server.conf";
595 const std::string folderpath = session_dir_ +
"/" + fqrn_;
596 MkdirDeep(folderpath.c_str(), 0700,
true );
598 const std::string session_config_file = folderpath +
"/server.conf";
599 const int fd_config = open(config_file.c_str(), O_RDONLY);
608 publisher =
new Publisher(*settings_publisher);
609 publisher->Transaction();
613 rvi = chdir(cwd.c_str());
622 "You can attach to this shell from another another terminal with\n");
623 if (options.
Has(
"root")) {
625 " nsenter --preserve-credentials -U -p -m -t %u\n"
627 anchor_pid.init_pid, rootfs_dir_.c_str());
630 " nsenter --preserve-credentials -U -m -t %u\n"
632 " nsenter --preserve-credentials -S %u -G %u -U -p -t 1\n",
633 anchor_pid.parent_pid, rootfs_dir_.c_str(), uid, gid);
636 std::vector<std::string> cmdline;
638 for (
unsigned i = 1; i < options.
plain_args().size(); ++i) {
639 cmdline.push_back(options.
plain_args()[i].value_str);
643 std::set<int> preserved_fds;
644 preserved_fds.insert(0);
645 preserved_fds.insert(1);
646 preserved_fds.insert(2);
648 rvb =
ManagedExec(cmdline, preserved_fds, std::map<int, int>(),
654 std::vector<int> sigs;
655 sigs.push_back(SIGUSR1);
659 if (options.
Has(
"transaction")
660 && !
FileExists(session_dir_ +
"/shellaction.marker")) {
662 publisher->session()->SetKeepAlive(
false);
671 if (!options.
Has(
"keep-session"))
672 CleanupSession(options.
Has(
"keep-logs"), new_paths);
std::string GetStringDefault(const std::string &key, const std::string &default_value) const
bool SymlinkForced(const std::string &src, const std::string &dest)
static void RemoveSingle(const std::string &path)
SettingsPublisher * CreateSettingsPublisher(const std::string &ident, bool needs_managed=false)
bool CreateMountNamespace()
static void EnsureDirectory(const std::string &path)
bool Has(const std::string &key) const
void CreateFile(const std::string &path, const int mode, const bool ignore_failure)
std::string GetString(const std::string &key) const
CVMFS_EXPORT const LogSource source
string JoinStrings(const vector< string > &strings, const string &joint)
bool ManagedExec(const std::vector< std::string > &command_line, const std::set< int > &preserve_fildes, const std::map< int, int > &map_fildes, const bool drop_credentials, const bool clear_env, const bool double_fork, pid_t *child_pid)
assert((mem||(size==0))&&"Out Of Memory")
static void RemoveUnderlay(const std::string &path, const std::vector< std::string > &new_paths)
bool SafeWriteToFile(const std::string &content, const std::string &path, int mode)
void SetConfigPath(const std::string &config_path)
std::string FindExecutable(const std::string &exe)
const std::vector< Argument > & plain_args() const
int WaitForChild(pid_t pid, const std::vector< int > &sig_ok)
void ParseDefault(const std::string &fqrn)
bool FileExists(const std::string &path)
void SetValue(const std::string &key, const std::string &value)
const int kPrivateFileMode
static AnchorPid EnterRootContainer()
ssize_t SafeRead(int fd, void *buf, size_t nbyte)
NamespaceFailures CreateUserNamespace(uid_t map_uid_to, gid_t map_gid_to)
bool ProcMount(const std::string &to)
bool MkdirDeep(const std::string &path, const mode_t mode, bool verify_writable)
std::string GetHomeDirectory()
string StringifyInt(const int64_t value)
std::string CreateTempDir(const std::string &path_prefix)
bool HasPrefix(const string &str, const string &prefix, const bool ignore_case)
bool CreatePidNamespace(int *fd_parent)
bool DirectoryExists(const std::string &path)
bool RemoveTree(const std::string &path)
bool SafeReadToString(int fd, std::string *final_result)
void ParsePath(const std::string &config_file, const bool external)
uint64_t String2Uint64(const string &value)
PathString GetParentPath(const PathString &path)
bool BindMount(const std::string &from, const std::string &to)
bool ListDirectory(const std::string &directory, std::vector< std::string > *names, std::vector< mode_t > *modes)
Publisher(const SettingsPublisher &settings, const bool exists=true)
bool ProcessExists(pid_t pid)
std::string GetCurrentWorkingDirectory()
CVMFS_EXPORT void LogCvmfs(const LogSource source, const int mask, const char *format,...)