blob: ff7c787c6f36bff537b5040a952f172bb3ae3cfe [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// MtabWatcherLinux implementation.
#include "components/storage_monitor/mtab_watcher_linux.h"
#include <mntent.h>
#include <stddef.h>
#include <stdio.h>
#include "base/bind.h"
#include "base/stl_util.h"
#include "base/threading/scoped_blocking_call.h"
namespace {
// List of file systems we care about.
const char* const kKnownFileSystems[] = {
"btrfs",
"ext2",
"ext3",
"ext4",
"fat",
"hfsplus",
"iso9660",
"msdos",
"ntfs",
"udf",
"vfat",
};
} // namespace
namespace storage_monitor {
MtabWatcherLinux::MtabWatcherLinux(const base::FilePath& mtab_path,
const UpdateMtabCallback& callback)
: mtab_path_(mtab_path), callback_(callback), weak_ptr_factory_(this) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
bool ret = file_watcher_.Watch(
mtab_path_, false,
base::Bind(&MtabWatcherLinux::OnFilePathChanged,
weak_ptr_factory_.GetWeakPtr()));
if (!ret) {
LOG(ERROR) << "Adding watch for " << mtab_path_.value() << " failed";
return;
}
ReadMtab();
}
MtabWatcherLinux::~MtabWatcherLinux() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void MtabWatcherLinux::ReadMtab() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::ScopedBlockingCall scoped_blocking_call(base::BlockingType::MAY_BLOCK);
FILE* fp = setmntent(mtab_path_.value().c_str(), "r");
if (!fp)
return;
MountPointDeviceMap device_map;
mntent entry;
char buf[512];
// We return the same device mounted to multiple locations, but hide
// devices that have been mounted over.
while (getmntent_r(fp, &entry, buf, sizeof(buf))) {
// We only care about real file systems.
for (size_t i = 0; i < base::size(kKnownFileSystems); ++i) {
if (strcmp(kKnownFileSystems[i], entry.mnt_type) == 0) {
device_map[base::FilePath(entry.mnt_dir)] =
base::FilePath(entry.mnt_fsname);
break;
}
}
}
endmntent(fp);
callback_.Run(device_map);
}
void MtabWatcherLinux::OnFilePathChanged(
const base::FilePath& path, bool error) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (path != mtab_path_) {
// This cannot happen unless FilePathWatcher is buggy. Just ignore this
// notification and do nothing.
NOTREACHED();
return;
}
if (error) {
LOG(ERROR) << "Error watching " << mtab_path_.value();
return;
}
ReadMtab();
}
} // namespace storage_monitor