blob: 2cd6195eb1b83674c3428550941a61478294d0f3 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <fcntl.h>
#include <unistd.h>
#include <sys/sendfile.h>
#include <sys/syscall.h>
#include <stout/stringify.hpp>
#include <stout/os/close.hpp>
#include <stout/os/open.hpp>
#include <stout/os/stat.hpp>
#include "linux/memfd.hpp"
using std::string;
#if !defined(F_ADD_SEALS)
#define F_ADD_SEALS 1033
#endif
#if !defined(F_GET_SEALS)
#define F_GET_SEALS 1034
#endif
#if !defined(F_SEAL_SEAL)
#define F_SEAL_SEAL 0x0001
#endif
#if !defined(F_SEAL_SHRINK)
#define F_SEAL_SHRINK 0x0002
#endif
#if !defined(F_SEAL_GROW)
#define F_SEAL_GROW 0x0004
#endif
#if !defined(F_SEAL_WRITE)
#define F_SEAL_WRITE 0x0008
#endif
namespace mesos {
namespace internal {
namespace memfd {
Try<int_fd> create(const string& name, unsigned int flags)
{
#ifdef __NR_memfd_create
int_fd fd = ::syscall(__NR_memfd_create, name.c_str(), flags);
if (fd == -1) {
return ErrnoError("Failed to create memfd");
}
return fd;
#else
#error "The memfd syscall is not available."
#endif
}
Try<int_fd> cloneSealedFile(const std::string& filePath)
{
if (!os::stat::isfile(filePath)) {
return Error("The original file '" + filePath + "' is not a regular file");
}
Try<Bytes> size = os::stat::size(filePath);
if (size.isError()) {
return Error("Failed to get the size of the source file: " + size.error());
}
Try<int_fd> fileFd = os::open(filePath, O_CLOEXEC | O_RDONLY);
if (fileFd.isError()) {
return Error("Failed to open source file: " + fileFd.error());
}
Try<int_fd> memFd = create(filePath, MFD_CLOEXEC | MFD_ALLOW_SEALING);
if (memFd.isError()) {
os::close(fileFd.get());
return Error("Failed to open memfd file: " + memFd.error());
}
size_t remaining = size->bytes();
while (remaining > 0) {
ssize_t written = sendfile(memFd.get(), fileFd.get(), nullptr, remaining);
if (written == -1) {
ErrnoError error("Failed to copy file");
os::close(fileFd.get());
os::close(memFd.get());
return error;
} else if (static_cast<size_t>(written) > remaining) {
os::close(fileFd.get());
os::close(memFd.get());
return Error("More bytes written than requested");
} else {
remaining -= written;
}
}
os::close(fileFd.get());
int ret = fchmod(memFd.get(), S_IRWXU | S_IRWXG | S_IRWXO);
if (ret == -1) {
ErrnoError error("Failed to chmod");
os::close(memFd.get());
return error;
}
// Seal the memfd file.
ret = fcntl(
memFd.get(),
F_ADD_SEALS,
F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL);
if (ret == -1) {
ErrnoError error("Failed to seal the memfd");
os::close(memFd.get());
return error;
}
return memFd.get();
}
} // namespace memfd {
} // namespace internal {
} // namespace mesos {