TError TMount::Find(TPath path, const TPath mounts) { path = path.NormalPath(); auto device = path.GetDev(); if (!device) return TError(EError::Unknown, "device not found: " + path.ToString() + ")"); FILE* f = setmntent(mounts.c_str(), "r"); if (!f) return TError(EError::Unknown, errno, "setmntent(" + mounts.ToString() + ")"); struct mntent* m, mntbuf; TScopedMem buf(4096); TError error(EError::Unknown, "mountpoint not found: " + path.ToString() + ")"); while ((m = getmntent_r(f, &mntbuf, (char *)buf.GetData(), buf.GetSize()))) { TPath source(m->mnt_fsname); TPath target(m->mnt_dir); if (target.InnerPath(path).IsEmpty() || source.GetBlockDev() != device) continue; Source = source; Target = target; Type = m->mnt_type; Data.clear(); error = SplitString(m->mnt_opts, ',', Data); break; } endmntent(f); return error; }
TError TMount::Move(TPath destination) { int ret = mount(Target.ToString().c_str(), destination.ToString().c_str(), NULL, MS_MOVE, NULL); if (ret) return TError(EError::Unknown, errno, "mount(" + Target.ToString() + ", " + destination.ToString() + ", MS_MOVE)"); Target = destination; return TError::Success(); }
TError TNamespaceFd::Open(TPath path) { Close(); Fd = open(path.c_str(), O_RDONLY | O_NOCTTY | O_NONBLOCK | O_CLOEXEC); if (Fd < 0) return TError(EError::Unknown, errno, "Cannot open " + path.ToString()); return TError::Success(); }
TError TTask::ChildMountDev() { struct { const std::string path; unsigned int mode; unsigned int dev; } node[] = { { "/dev/null", 0666 | S_IFCHR, MKDEV(1, 3) }, { "/dev/zero", 0666 | S_IFCHR, MKDEV(1, 5) }, { "/dev/full", 0666 | S_IFCHR, MKDEV(1, 7) }, { "/dev/random", 0666 | S_IFCHR, MKDEV(1, 8) }, { "/dev/urandom", 0666 | S_IFCHR, MKDEV(1, 9) }, }; TMount dev("tmpfs", Env->Root + "/dev", "tmpfs", { "mode=755", "size=32m" }); TError error = dev.MountDir(MS_NOSUID | MS_STRICTATIME); if (error) return error; TMount devpts("devpts", Env->Root + "/dev/pts", "devpts", { "newinstance", "ptmxmode=0666", "mode=620" ,"gid=5" }); error = devpts.MountDir(MS_NOSUID | MS_NOEXEC); if (error) return error; for (size_t i = 0; i < sizeof(node) / sizeof(node[0]); i++) { error = ChildCreateNode(Env->Root + node[i].path, node[i].mode, node[i].dev); if (error) return error; } TPath ptmx = Env->Root + "/dev/ptmx"; if (symlink("pts/ptmx", ptmx.ToString().c_str()) < 0) return TError(EError::Unknown, errno, "symlink(/dev/pts/ptmx)"); TPath fd = Env->Root + "/dev/fd"; if (symlink("/proc/self/fd", fd.ToString().c_str()) < 0) return TError(EError::Unknown, errno, "symlink(/dev/fd)"); TFile f(Env->Root + "/dev/console", 0755); (void)f.Touch(); return TError::Success(); }
std::string ReadLink(const std::string &path) { TPath lnk; TPath f(path); TError error = f.ReadLink(lnk); if (error) throw error.GetMsg(); return lnk.ToString(); }
TError TTask::ChildOpenStdFile(const TPath &path, int expected) { int ret = open(path.ToString().c_str(), O_CREAT | O_WRONLY | O_APPEND, 0660); if (ret < 0) return TError(EError::InvalidValue, errno, "open(" + path.ToString() + ") -> " + std::to_string(expected)); if (ret != expected) return TError(EError::Unknown, EINVAL, "open(" + path.ToString() + ") -> " + std::to_string(expected) + ": unexpected fd " + std::to_string(ret)); ret = fchown(ret, Env->Cred.Uid, Env->Cred.Gid); if (ret < 0) return TError(EError::Unknown, errno, "fchown(" + path.ToString() + ") -> " + std::to_string(expected)); return TError::Success(); }
void BootstrapCommand(const std::string &cmd, const TPath &path, bool remove) { if (remove) (void)path.RemoveAll(); vector<string> lines; ExpectSuccess(Popen("ldd " + cmd, lines)); for (auto &line : lines) { vector<string> tokens; TError error = SplitString(line, ' ', tokens); if (error) throw error.GetMsg(); TPath from; string name; if (tokens.size() == 2) { from = StringTrim(tokens[0]); TPath p(tokens[0]); name = p.BaseName(); } else if (tokens.size() == 4) { if (tokens[2] == "") continue; from = StringTrim(tokens[2]); name = StringTrim(tokens[0]); } else { continue; } TPath dest = path / from.DirName(); if (!dest.Exists()) { error = dest.MkdirAll(0755); if (error) throw error.GetMsg(); } Expect(system(("cp " + from.ToString() + " " + dest.ToString() + "/" + name).c_str()) == 0); } Expect(system(("cp " + cmd + " " + path.ToString()).c_str()) == 0); }
TError TTask::CreateTmpDir(const TPath &path, std::shared_ptr<TFolder> &dir) const { bool cleanup = path.ToString().find(config().container().tmp_dir()) == 0; dir = std::make_shared<TFolder>(path, cleanup); if (!dir->Exists()) { TError error = dir->Create(0755, true); if (error) return error; error = path.Chown(Env->Cred.Uid, Env->Cred.Gid); if (error) return error; } return TError::Success(); }
TError TMount::Snapshot(std::vector<std::shared_ptr<TMount>> &result, const TPath mounts) { FILE* f = setmntent(mounts.c_str(), "r"); if (!f) return TError(EError::Unknown, errno, "setmntent(" + mounts.ToString() + ")"); struct mntent* m, mntbuf; TScopedMem buf(4096); while ((m = getmntent_r(f, &mntbuf, (char *)buf.GetData(), buf.GetSize()))) { vector<string> flags; TError error = SplitString(m->mnt_opts, ',', flags); if (error) { endmntent(f); return error; } result.push_back(std::make_shared<TMount>(m->mnt_fsname, m->mnt_dir, m->mnt_type, flags)); } endmntent(f); return TError::Success(); }
TError TTask::ChildCreateNode(const TPath &path, unsigned int mode, unsigned int dev) { if (mknod(path.ToString().c_str(), mode, dev) < 0) return TError(EError::Unknown, errno, "mknod(" + path.ToString() + ")"); return TError::Success(); }
TError SetupLoopDevice(TPath image, int &dev) { static std::mutex BigLoopLock; int control_fd, image_fd, loop_nr, loop_fd; struct loop_info64 info; std::string loop; int retry = 10; TError error; image_fd = open(image.c_str(), O_RDWR | O_CLOEXEC); if (image_fd < 0) { error = TError(EError::Unknown, errno, "open(" + image.ToString() + ")"); goto err_image; } control_fd = open("/dev/loop-control", O_RDWR | O_CLOEXEC); if (control_fd < 0) { error = TError(EError::Unknown, errno, "open(/dev/loop-control)"); goto err_control; } BigLoopLock.lock(); again: loop_nr = ioctl(control_fd, LOOP_CTL_GET_FREE); if (loop_nr < 0) { error = TError(EError::Unknown, errno, "ioctl(LOOP_CTL_GET_FREE)"); goto err_get_free; } loop = "/dev/loop" + std::to_string(loop_nr); loop_fd = open(loop.c_str(), O_RDWR | O_CLOEXEC); if (loop_fd < 0) { error = TError(EError::Unknown, errno, "open(" + loop + ")"); goto err_loop_open; } if (ioctl(loop_fd, LOOP_SET_FD, image_fd) < 0) { error = TError(EError::Unknown, errno, "ioctl(LOOP_SET_FD)"); if (errno == EBUSY) { if (!ioctl(loop_fd, LOOP_GET_STATUS64, &info) || errno == ENXIO) { if (--retry > 0) { close(loop_fd); goto again; } } } goto err_set_fd; } memset(&info, 0, sizeof(info)); strncpy((char *)info.lo_file_name, image.c_str(), LO_NAME_SIZE); if (ioctl(loop_fd, LOOP_SET_STATUS64, &info) < 0) { error = TError(EError::Unknown, errno, "ioctl(LOOP_SET_STATUS64)"); ioctl(loop_fd, LOOP_CLR_FD, 0); goto err_set_status; } dev = loop_nr; error = TError::Success(); err_set_status: err_set_fd: close(loop_fd); err_loop_open: err_get_free: BigLoopLock.unlock(); close(control_fd); err_control: close(image_fd); err_image: return error; }
TError TMount::Remount(TPath path, unsigned long flags) { if (mount(NULL, path.c_str(), NULL, flags, NULL)) return TError(EError::Unknown, errno, "mount(NULL, " + path.ToString() + ", NULL, " + std::to_string(flags) + ", NULL)"); return TError::Success(); }
friend bool operator>(const TPath& a, const TPath& b) { return a.ToString() > b.ToString(); }
friend bool operator!=(const TPath& a, const TPath& b) { return a.ToString() != b.ToString(); }
TPath operator+(const TPath &p) const { return TPath(Path + p.ToString()); }