1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/filesystem/file_impl.h"
10 #include <sys/types.h>
15 #include "base/files/scoped_file.h"
16 #include "base/logging.h"
17 #include "base/posix/eintr_wrapper.h"
18 #include "components/filesystem/shared_impl.h"
19 #include "components/filesystem/util.h"
21 static_assert(sizeof(off_t
) <= sizeof(int64_t), "off_t too big");
22 static_assert(sizeof(size_t) >= sizeof(uint32_t), "size_t too small");
24 namespace filesystem
{
26 const size_t kMaxReadSize
= 1 * 1024 * 1024; // 1 MB.
28 FileImpl::FileImpl(mojo::InterfaceRequest
<File
> request
, base::ScopedFD file_fd
)
29 : binding_(this, request
.Pass()), file_fd_(file_fd
.Pass()) {
30 DCHECK(file_fd_
.is_valid());
33 FileImpl::~FileImpl() {
36 void FileImpl::Close(const CloseCallback
& callback
) {
37 if (!file_fd_
.is_valid()) {
38 callback
.Run(ERROR_CLOSED
);
41 int fd_to_try_to_close
= file_fd_
.release();
42 // POSIX.1 (2013) leaves the validity of the FD undefined on EINTR and EIO. On
43 // Linux, the FD is always invalidated, so we'll pretend that the close
44 // succeeded. (On other Unixes, the situation may be different and possibly
45 // totally broken; see crbug.com/269623.)
46 if (IGNORE_EINTR(close(fd_to_try_to_close
)) != 0) {
47 // Save errno, since we do a few things and we don't want it trampled.
49 CHECK_NE(error
, EBADF
); // This should never happen.
50 DCHECK_NE(error
, EINTR
); // We already ignored EINTR.
51 // I don't know what Linux does on EIO (or any other errors) -- POSIX leaves
52 // it undefined -- so report the error and hope that the FD was invalidated.
53 callback
.Run(ErrnoToError(error
));
57 callback
.Run(ERROR_OK
);
60 // TODO(vtl): Move the implementation to a thread pool.
61 void FileImpl::Read(uint32_t num_bytes_to_read
,
64 const ReadCallback
& callback
) {
65 if (!file_fd_
.is_valid()) {
66 callback
.Run(ERROR_CLOSED
, mojo::Array
<uint8_t>());
69 if (num_bytes_to_read
> kMaxReadSize
) {
70 callback
.Run(ERROR_OUT_OF_RANGE
, mojo::Array
<uint8_t>());
73 if (Error error
= IsOffsetValid(offset
)) {
74 callback
.Run(error
, mojo::Array
<uint8_t>());
77 if (Error error
= IsWhenceValid(whence
)) {
78 callback
.Run(error
, mojo::Array
<uint8_t>());
82 if (offset
!= 0 || whence
!= WHENCE_FROM_CURRENT
) {
83 // TODO(vtl): Use |pread()| below in the |WHENCE_FROM_START| case. This
84 // implementation is obviously not atomic. (If someone seeks simultaneously,
85 // we'll end up writing somewhere else. Or, well, we would if we were
86 // multithreaded.) Maybe we should do an |ftell()| and always use |pread()|.
87 // TODO(vtl): Possibly, at least sometimes we should not change the file
88 // position. See TODO in file.mojom.
89 if (lseek(file_fd_
.get(), static_cast<off_t
>(offset
),
90 WhenceToStandardWhence(whence
)) < 0) {
91 callback
.Run(ErrnoToError(errno
), mojo::Array
<uint8_t>());
96 mojo::Array
<uint8_t> bytes_read(num_bytes_to_read
);
97 ssize_t num_bytes_read
= HANDLE_EINTR(
98 read(file_fd_
.get(), &bytes_read
.front(), num_bytes_to_read
));
99 if (num_bytes_read
< 0) {
100 callback
.Run(ErrnoToError(errno
), mojo::Array
<uint8_t>());
104 DCHECK_LE(static_cast<size_t>(num_bytes_read
), num_bytes_to_read
);
105 bytes_read
.resize(static_cast<size_t>(num_bytes_read
));
106 callback
.Run(ERROR_OK
, bytes_read
.Pass());
109 // TODO(vtl): Move the implementation to a thread pool.
110 void FileImpl::Write(mojo::Array
<uint8_t> bytes_to_write
,
113 const WriteCallback
& callback
) {
114 DCHECK(!bytes_to_write
.is_null());
116 if (!file_fd_
.is_valid()) {
117 callback
.Run(ERROR_CLOSED
, 0);
120 // Who knows what |write()| would return if the size is that big (and it
121 // actually wrote that much).
122 if (bytes_to_write
.size() >
123 static_cast<size_t>(std::numeric_limits
<ssize_t
>::max())) {
124 callback
.Run(ERROR_OUT_OF_RANGE
, 0);
127 if (Error error
= IsOffsetValid(offset
)) {
128 callback
.Run(error
, 0);
131 if (Error error
= IsWhenceValid(whence
)) {
132 callback
.Run(error
, 0);
136 if (offset
!= 0 || whence
!= WHENCE_FROM_CURRENT
) {
137 // TODO(vtl): Use |pwrite()| below in the |WHENCE_FROM_START| case. This
138 // implementation is obviously not atomic. (If someone seeks simultaneously,
139 // we'll end up writing somewhere else. Or, well, we would if we were
140 // multithreaded.) Maybe we should do an |ftell()| and always use
142 // TODO(vtl): Possibly, at least sometimes we should not change the file
143 // position. See TODO in file.mojom.
144 if (lseek(file_fd_
.get(), static_cast<off_t
>(offset
),
145 WhenceToStandardWhence(whence
)) < 0) {
146 callback
.Run(ErrnoToError(errno
), 0);
152 (bytes_to_write
.size() > 0) ? &bytes_to_write
.front() : nullptr;
153 ssize_t num_bytes_written
=
154 HANDLE_EINTR(write(file_fd_
.get(), buf
, bytes_to_write
.size()));
155 if (num_bytes_written
< 0) {
156 callback
.Run(ErrnoToError(errno
), 0);
160 DCHECK_LE(static_cast<size_t>(num_bytes_written
),
161 std::numeric_limits
<uint32_t>::max());
162 callback
.Run(ERROR_OK
, static_cast<uint32_t>(num_bytes_written
));
165 void FileImpl::ReadToStream(mojo::ScopedDataPipeProducerHandle source
,
168 int64_t num_bytes_to_read
,
169 const ReadToStreamCallback
& callback
) {
170 if (!file_fd_
.is_valid()) {
171 callback
.Run(ERROR_CLOSED
);
174 if (Error error
= IsOffsetValid(offset
)) {
178 if (Error error
= IsWhenceValid(whence
)) {
183 // TODO(vtl): FIXME soon
185 callback
.Run(ERROR_UNIMPLEMENTED
);
188 void FileImpl::WriteFromStream(mojo::ScopedDataPipeConsumerHandle sink
,
191 const WriteFromStreamCallback
& callback
) {
192 if (!file_fd_
.is_valid()) {
193 callback
.Run(ERROR_CLOSED
);
196 if (Error error
= IsOffsetValid(offset
)) {
200 if (Error error
= IsWhenceValid(whence
)) {
205 // TODO(vtl): FIXME soon
207 callback
.Run(ERROR_UNIMPLEMENTED
);
210 void FileImpl::Tell(const TellCallback
& callback
) {
211 Seek(0, WHENCE_FROM_CURRENT
, callback
);
214 void FileImpl::Seek(int64_t offset
,
216 const SeekCallback
& callback
) {
217 if (!file_fd_
.is_valid()) {
218 callback
.Run(ERROR_CLOSED
, 0);
221 if (Error error
= IsOffsetValid(offset
)) {
222 callback
.Run(error
, 0);
225 if (Error error
= IsWhenceValid(whence
)) {
226 callback
.Run(error
, 0);
230 off_t position
= lseek(file_fd_
.get(), static_cast<off_t
>(offset
),
231 WhenceToStandardWhence(whence
));
233 callback
.Run(ErrnoToError(errno
), 0);
237 callback
.Run(ERROR_OK
, static_cast<int64
>(position
));
240 void FileImpl::Stat(const StatCallback
& callback
) {
241 if (!file_fd_
.is_valid()) {
242 callback
.Run(ERROR_CLOSED
, nullptr);
245 StatFD(file_fd_
.get(), FILE_TYPE_REGULAR_FILE
, callback
);
248 void FileImpl::Truncate(int64_t size
, const TruncateCallback
& callback
) {
249 if (!file_fd_
.is_valid()) {
250 callback
.Run(ERROR_CLOSED
);
254 callback
.Run(ERROR_INVALID_ARGUMENT
);
257 if (Error error
= IsOffsetValid(size
)) {
262 if (ftruncate(file_fd_
.get(), static_cast<off_t
>(size
)) != 0) {
263 callback
.Run(ErrnoToError(errno
));
267 callback
.Run(ERROR_OK
);
270 void FileImpl::Touch(TimespecOrNowPtr atime
,
271 TimespecOrNowPtr mtime
,
272 const TouchCallback
& callback
) {
273 if (!file_fd_
.is_valid()) {
274 callback
.Run(ERROR_CLOSED
);
277 TouchFD(file_fd_
.get(), atime
.Pass(), mtime
.Pass(), callback
);
280 void FileImpl::Dup(mojo::InterfaceRequest
<File
> file
,
281 const DupCallback
& callback
) {
282 if (!file_fd_
.is_valid()) {
283 callback
.Run(ERROR_CLOSED
);
287 base::ScopedFD
file_fd(dup(file_fd_
.get()));
288 if (!file_fd
.is_valid()) {
289 callback
.Run(ErrnoToError(errno
));
293 new FileImpl(file
.Pass(), file_fd
.Pass());
294 callback
.Run(ERROR_OK
);
297 void FileImpl::Reopen(mojo::InterfaceRequest
<File
> file
,
299 const ReopenCallback
& callback
) {
300 if (!file_fd_
.is_valid()) {
301 callback
.Run(ERROR_CLOSED
);
305 // TODO(vtl): FIXME soon
307 callback
.Run(ERROR_UNIMPLEMENTED
);
310 void FileImpl::AsBuffer(const AsBufferCallback
& callback
) {
311 if (!file_fd_
.is_valid()) {
312 callback
.Run(ERROR_CLOSED
, mojo::ScopedSharedBufferHandle());
316 // TODO(vtl): FIXME soon
318 callback
.Run(ERROR_UNIMPLEMENTED
, mojo::ScopedSharedBufferHandle());
321 void FileImpl::Ioctl(uint32_t request
,
322 mojo::Array
<uint32_t> in_values
,
323 const IoctlCallback
& callback
) {
324 if (!file_fd_
.is_valid()) {
325 callback
.Run(ERROR_CLOSED
, mojo::Array
<uint32_t>());
329 // TODO(vtl): The "correct" error code should be one that can be translated to
331 callback
.Run(ERROR_UNAVAILABLE
, mojo::Array
<uint32_t>());
334 } // namespace filesystem