chromium/chrome/browser/ash/crostini/crostini_sshfs.h

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_ASH_CROSTINI_CROSTINI_SSHFS_H_
#define CHROME_BROWSER_ASH_CROSTINI_CROSTINI_SSHFS_H_

#include <queue>
#include <set>
#include <utility>

#include "base/files/file_path.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "base/time/time.h"
#include "chrome/browser/ash/crostini/crostini_manager.h"
#include "chromeos/ash/components/disks/disk_mount_manager.h"

namespace crostini {

class CrostiniSshfs : ContainerShutdownObserver {
 public:
  explicit CrostiniSshfs(Profile* profile);

  CrostiniSshfs(const CrostiniSshfs&) = delete;
  CrostiniSshfs& operator=(const CrostiniSshfs&) = delete;

  ~CrostiniSshfs() override;
  using MountCrostiniFilesCallback = base::OnceCallback<void(bool succeeded)>;

  // Mounts the user's Crostini home directory so it's accessible from the host.
  // Must be called from the UI thread, no-op if the home directory is already
  // mounted. If this is something running in the background set background to
  // true, if failures are user-visible set it to false. If you're setting
  // base::DoNothing as the callback then background should be true.
  void MountCrostiniFiles(const guest_os::GuestId& container_id,
                          MountCrostiniFilesCallback callback,
                          bool background);

  // Unmounts the user's Crostini home directory. Must be called from the UI
  // thread.
  void UnmountCrostiniFiles(const guest_os::GuestId& container_id,
                            MountCrostiniFilesCallback callback);

  // ContainerShutdownObserver.
  void OnContainerShutdown(const guest_os::GuestId& container_id) override;

  void OnMountEvent(ash::MountError error_code,
                    const ash::disks::DiskMountManager::MountPoint& mount_info);

  // Returns true if sshfs is mounted for the specified container, else false.
  bool IsSshfsMounted(const guest_os::GuestId& container);

  // Only public so unit tests can reference them without needing to FRIEND_TEST
  // every single test case.
  // These values are persisted to logs. Entries should not be renumbered and
  // numeric values should never be reused.
  enum class CrostiniSshfsResult {
    kSuccess = 0,
    kNotDefaultContainer = 1,
    kContainerNotRunning = 2,
    kGetSshKeysFailed = 3,
    kGetContainerInfoFailed = 4,
    kMountErrorInternal = 5,
    kMountErrorProgramFailed = 6,
    kMountErrorOther = 7,
    kMaxValue = kMountErrorOther,
  };

 private:
  void SetSshfsMounted(const guest_os::GuestId& container, bool mounted);
  void Finish(CrostiniSshfsResult result);

  void OnRemoveSshfsCrostiniVolume(const guest_os::GuestId& container_id,
                                   MountCrostiniFilesCallback callback,
                                   base::Time started,
                                   bool success);

  struct InProgressMount {
    std::string source_path;
    guest_os::GuestId container_id;
    base::FilePath container_homedir;
    MountCrostiniFilesCallback callback;
    base::Time started;
    bool background;
    InProgressMount(const guest_os::GuestId& container,
                    MountCrostiniFilesCallback callback,
                    bool background);
    InProgressMount(InProgressMount&& other) noexcept;
    InProgressMount& operator=(InProgressMount&& other) noexcept;
    ~InProgressMount();
  };
  struct PendingRequest {
    guest_os::GuestId container_id;
    MountCrostiniFilesCallback callback;
    bool background;
    PendingRequest(const guest_os::GuestId& container_id,
                   MountCrostiniFilesCallback callback,
                   bool background);
    PendingRequest(PendingRequest&& other) noexcept;
    PendingRequest& operator=(PendingRequest&& other) noexcept;
    ~PendingRequest();
  };
  raw_ptr<Profile> profile_;

  base::ScopedObservation<CrostiniManager, ContainerShutdownObserver>
      container_shutdown_observer_{this};

  std::unique_ptr<InProgressMount> in_progress_mount_;

  std::set<guest_os::GuestId> sshfs_mounted_;
  std::queue<PendingRequest> pending_requests_;

  // Note: This should remain the last member so it'll be destroyed and
  // invalidate its weak pointers before any other members are destroyed.
  base::WeakPtrFactory<CrostiniSshfs> weak_ptr_factory_{this};
};
}  // namespace crostini

#endif  // CHROME_BROWSER_ASH_CROSTINI_CROSTINI_SSHFS_H_