aboutsummaryrefslogtreecommitdiffstats
path: root/brillo/namespaces/mount_namespace.cc
blob: 1944983ec65df11aab7b0ed9faa1f5d023428d0f (plain)
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
// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Contains the implementation of class MountNamespace for libbrillo.

#include "brillo/namespaces/mount_namespace.h"

#include <sched.h>
#include <sys/mount.h>
#include <sys/types.h>

#include <string>

#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/strings/stringprintf.h>
#include <brillo/namespaces/platform.h>

namespace brillo {
MountNamespace::MountNamespace(const base::FilePath& ns_path,
                               Platform* platform)
    : ns_path_(ns_path), platform_(platform), exists_(false) {}

MountNamespace::~MountNamespace() {
  if (exists_)
    Destroy();
}

bool MountNamespace::Create() {
  if (platform_->FileSystemIsNsfs(ns_path_)) {
    LOG(ERROR) << "Mount namespace at " << ns_path_.value()
               << " already exists.";
    return false;
  }
  int fd_mounted[2];
  int fd_unshared[2];
  char byte = '\0';
  if (pipe(fd_mounted) != 0) {
    PLOG(ERROR) << "Cannot create mount signalling pipe";
    return false;
  }
  if (pipe(fd_unshared) != 0) {
    PLOG(ERROR) << "Cannot create unshare signalling pipe";
    return false;
  }
  pid_t pid = platform_->Fork();
  if (pid < 0) {
    PLOG(ERROR) << "Fork failed";
  } else if (pid == 0) {
    // Child.
    close(fd_mounted[1]);
    close(fd_unshared[0]);
    if (unshare(CLONE_NEWNS) != 0) {
      PLOG(ERROR) << "unshare(CLONE_NEWNS) failed";
      exit(1);
    }
    base::WriteFileDescriptor(fd_unshared[1], &byte, 1);
    base::ReadFromFD(fd_mounted[0], &byte, 1);
    exit(0);
  } else {
    // Parent.
    close(fd_mounted[0]);
    close(fd_unshared[1]);
    std::string proc_ns_path = base::StringPrintf("/proc/%d/ns/mnt", pid);
    bool mount_success = true;
    base::ReadFromFD(fd_unshared[0], &byte, 1);
    if (platform_->Mount(proc_ns_path, ns_path_.value(), "", MS_BIND) != 0) {
      PLOG(ERROR) << "Mount(" << proc_ns_path << ", " << ns_path_.value()
                  << ", MS_BIND) failed";
      mount_success = false;
    }
    base::WriteFileDescriptor(fd_mounted[1], &byte, 1);

    int status;
    if (platform_->Waitpid(pid, &status) < 0) {
      PLOG(ERROR) << "waitpid(" << pid << ") failed";
      return false;
    }
    if (!WIFEXITED(status)) {
      LOG(ERROR) << "Child process did not exit normally.";
    } else if (WEXITSTATUS(status) != 0) {
      LOG(ERROR) << "Child process failed.";
    } else {
      exists_ = mount_success;
    }
  }
  return exists_;
}

bool MountNamespace::Destroy() {
  if (!exists_) {
    LOG(ERROR) << "Mount namespace at " << ns_path_.value()
               << "does not exist, cannot destroy";
    return false;
  }
  bool was_busy;
  if (!platform_->Unmount(ns_path_, false /*lazy*/, &was_busy)) {
    PLOG(ERROR) << "Failed to unmount " << ns_path_.value();
    if (was_busy) {
      LOG(ERROR) << ns_path_.value().c_str() << " was busy";
    }
    // If Unmount() fails, keep the object valid by keeping |exists_|
    // set to true.
    return false;
  } else {
    VLOG(1) << "Unmounted namespace at " << ns_path_.value();
  }
  exists_ = false;
  return true;
}

}  // namespace brillo