blob: 45d43b76aef7867dfc08dc72bdb527797c57b1ae [file] [log] [blame]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "fusebox/built_in.h"
#include <inttypes.h>
#include <utility>
#include <base/logging.h>
#include <base/time/time.h>
#include <chromeos/dbus/service_constants.h>
#include <dbus/message.h>
#include "fusebox/make_stat.h"
namespace fusebox {
namespace {
// kFuseStatusContentsLen equals strlen(kFuseStatusContentsPtr).
constexpr size_t kFuseStatusContentsLen = 3;
constexpr char kFuseStatusContentsPtr[] = "ok\n";
constexpr char kFuseStatusFilename[] = "fuse_status";
// INT64_MAX, 9223372036854775807, has 19 digits. The " micros\n" suffix adds a
// further 8 bytes.
constexpr size_t kPingContentsLen = 27;
constexpr char kPingFilename[] = "ping";
void clipToBounds(size_t& size, off_t& off, size_t max_size) {
if ((off < 0) || (max_size <= off)) {
size = 0;
off = 0;
} else if (size > (max_size - off)) {
size = max_size - off;
}
}
void BuiltInReadPingResponse(std::unique_ptr<BufferRequest> request,
base::TimeTicks start_ticks,
size_t size,
off_t off,
dbus::Response* response) {
int64_t micros = (base::TimeTicks::Now() - start_ticks).InMicroseconds();
if (micros < 0) {
micros = 0;
}
char buffer[32];
snprintf(buffer, sizeof(buffer), "%19" PRId64 " micros\n", micros);
CHECK_EQ(kPingContentsLen, strlen(buffer));
clipToBounds(size, off, kPingContentsLen);
request->ReplyBuffer(buffer + off, size);
}
void BuiltInReadPing(scoped_refptr<dbus::ObjectProxy> dbus_proxy,
std::unique_ptr<BufferRequest> request,
size_t size,
off_t off) {
// Issue a Stat2 call over D-Bus. The result of the Stat2 doesn't matter (and
// should be an error for a subdir that doesn't exist). We just want to time
// how long it takes to respond.
Stat2RequestProto request_proto;
request_proto.set_file_system_url("ping_subdir_should_not_exist");
dbus::MethodCall method(kFuseBoxServiceInterface, kStat2Method);
dbus::MessageWriter writer(&method);
writer.AppendProtoAsArrayOfBytes(request_proto);
dbus_proxy->CallMethod(
&method, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&BuiltInReadPingResponse, std::move(request),
base::TimeTicks::Now(), size, off));
}
} // namespace
void BuiltInEnsureNodes(InodeTable& itab) {
itab.Ensure(INO_BUILT_IN, kFuseStatusFilename, INO_BUILT_IN_FUSE_STATUS);
itab.Ensure(INO_BUILT_IN, kPingFilename, INO_BUILT_IN_PING);
}
void BuiltInGetStat(ino_t ino, struct stat* stat) {
*stat = {0};
switch (ino) {
case INO_BUILT_IN_FUSE_STATUS:
stat->st_size = kFuseStatusContentsLen;
break;
case INO_BUILT_IN_PING:
stat->st_size = kPingContentsLen;
break;
default:
return;
}
stat->st_dev = 1;
stat->st_ino = ino;
stat->st_mode = S_IFREG | 0444;
stat->st_nlink = 1;
stat->st_uid = kChronosUID;
stat->st_gid = kChronosAccessGID;
}
void BuiltInLookup(std::unique_ptr<EntryRequest> request,
const std::string& name) {
ino_t ino = 0;
if (name == kFuseStatusFilename) {
ino = INO_BUILT_IN_FUSE_STATUS;
} else if (name == kPingFilename) {
ino = INO_BUILT_IN_PING;
} else {
errno = request->ReplyError(ENOENT);
PLOG(ERROR) << "BuiltInLookup";
return;
}
fuse_entry_param entry = {0};
entry.ino = ino;
BuiltInGetStat(ino, &entry.attr);
entry.attr_timeout = kStatTimeoutSeconds;
entry.entry_timeout = kEntryTimeoutSeconds;
request->ReplyEntry(entry);
}
void BuiltInRead(scoped_refptr<dbus::ObjectProxy> dbus_proxy,
std::unique_ptr<BufferRequest> request,
ino_t ino,
size_t size,
off_t off) {
switch (ino) {
case INO_BUILT_IN_FUSE_STATUS:
clipToBounds(size, off, kFuseStatusContentsLen);
request->ReplyBuffer(kFuseStatusContentsPtr + off, size);
return;
case INO_BUILT_IN_PING:
BuiltInReadPing(std::move(dbus_proxy), std::move(request), size, off);
return;
}
errno = request->ReplyError(ENOENT);
PLOG(ERROR) << "BuiltInRead";
}
void BuiltInReadDir(off_t off, std::unique_ptr<DirEntryRequest> request) {
static const std::pair<ino_t, const char*> entries[] = {
{INO_BUILT_IN_FUSE_STATUS, kFuseStatusFilename},
{INO_BUILT_IN_PING, kPingFilename},
};
static constexpr size_t n = sizeof(entries) / sizeof(entries[0]);
for (size_t i = off; i < n; i++) {
if (!request->AddEntry(
{entries[i].first, entries[i].second, S_IFREG | 0444}, i + 1)) {
break;
}
}
request->ReplyDone();
}
} // namespace fusebox