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 "base/files/file_path.h"
11 #include "base/files/scoped_file.h"
12 #include "base/logging.h"
13 #include "components/filesystem/util.h"
14 #include "mojo/platform_handle/platform_handle_functions.h"
16 static_assert(sizeof(off_t
) <= sizeof(int64_t), "off_t too big");
17 static_assert(sizeof(size_t) >= sizeof(uint32_t), "size_t too small");
20 using mojo::ScopedHandle
;
22 namespace filesystem
{
24 const size_t kMaxReadSize
= 1 * 1024 * 1024; // 1 MB.
26 FileImpl::FileImpl(mojo::InterfaceRequest
<File
> request
,
27 const base::FilePath
& path
,
29 : binding_(this, request
.Pass()), file_(path
, flags
) {
30 DCHECK(file_
.IsValid());
33 FileImpl::FileImpl(mojo::InterfaceRequest
<File
> request
, base::File file
)
34 : binding_(this, request
.Pass()), file_(file
.Pass()) {
35 DCHECK(file_
.IsValid());
38 FileImpl::~FileImpl() {
41 void FileImpl::Close(const CloseCallback
& callback
) {
42 if (!file_
.IsValid()) {
43 callback
.Run(GetError(file_
));
48 callback
.Run(FILE_ERROR_OK
);
51 // TODO(vtl): Move the implementation to a thread pool.
52 void FileImpl::Read(uint32_t num_bytes_to_read
,
55 const ReadCallback
& callback
) {
56 if (!file_
.IsValid()) {
57 callback
.Run(GetError(file_
), mojo::Array
<uint8_t>());
60 if (num_bytes_to_read
> kMaxReadSize
) {
61 callback
.Run(FILE_ERROR_INVALID_OPERATION
, mojo::Array
<uint8_t>());
64 if (FileError error
= IsOffsetValid(offset
)) {
65 callback
.Run(error
, mojo::Array
<uint8_t>());
68 if (FileError error
= IsWhenceValid(whence
)) {
69 callback
.Run(error
, mojo::Array
<uint8_t>());
73 if (file_
.Seek(static_cast<base::File::Whence
>(whence
), offset
) == -1) {
74 callback
.Run(FILE_ERROR_FAILED
, mojo::Array
<uint8_t>());
78 mojo::Array
<uint8_t> bytes_read(num_bytes_to_read
);
79 int num_bytes_read
= file_
.ReadAtCurrentPos(
80 reinterpret_cast<char*>(&bytes_read
.front()), num_bytes_to_read
);
81 if (num_bytes_read
< 0) {
82 callback
.Run(FILE_ERROR_FAILED
, mojo::Array
<uint8_t>());
86 DCHECK_LE(static_cast<size_t>(num_bytes_read
), num_bytes_to_read
);
87 bytes_read
.resize(static_cast<size_t>(num_bytes_read
));
88 callback
.Run(FILE_ERROR_OK
, bytes_read
.Pass());
91 // TODO(vtl): Move the implementation to a thread pool.
92 void FileImpl::Write(mojo::Array
<uint8_t> bytes_to_write
,
95 const WriteCallback
& callback
) {
96 DCHECK(!bytes_to_write
.is_null());
97 if (!file_
.IsValid()) {
98 callback
.Run(GetError(file_
), 0);
101 // Who knows what |write()| would return if the size is that big (and it
102 // actually wrote that much).
103 if (bytes_to_write
.size() >
105 static_cast<size_t>(std::numeric_limits
<int>::max())) {
107 static_cast<size_t>(std::numeric_limits
<ssize_t
>::max())) {
109 callback
.Run(FILE_ERROR_INVALID_OPERATION
, 0);
112 if (FileError error
= IsOffsetValid(offset
)) {
113 callback
.Run(error
, 0);
116 if (FileError error
= IsWhenceValid(whence
)) {
117 callback
.Run(error
, 0);
121 if (file_
.Seek(static_cast<base::File::Whence
>(whence
), offset
) == -1) {
122 callback
.Run(FILE_ERROR_FAILED
, 0);
126 const char* buf
= (bytes_to_write
.size() > 0)
127 ? reinterpret_cast<char*>(&bytes_to_write
.front())
129 int num_bytes_written
= file_
.WriteAtCurrentPos(
130 buf
, static_cast<int>(bytes_to_write
.size()));
131 if (num_bytes_written
< 0) {
132 callback
.Run(FILE_ERROR_FAILED
, 0);
136 DCHECK_LE(static_cast<size_t>(num_bytes_written
),
137 std::numeric_limits
<uint32_t>::max());
138 callback
.Run(FILE_ERROR_OK
, static_cast<uint32_t>(num_bytes_written
));
141 void FileImpl::Tell(const TellCallback
& callback
) {
142 Seek(0, WHENCE_FROM_CURRENT
, callback
);
145 void FileImpl::Seek(int64_t offset
,
147 const SeekCallback
& callback
) {
148 if (!file_
.IsValid()) {
149 callback
.Run(GetError(file_
), 0);
152 if (FileError error
= IsOffsetValid(offset
)) {
153 callback
.Run(error
, 0);
156 if (FileError error
= IsWhenceValid(whence
)) {
157 callback
.Run(error
, 0);
161 int64 position
= file_
.Seek(static_cast<base::File::Whence
>(whence
), offset
);
163 callback
.Run(FILE_ERROR_FAILED
, 0);
167 callback
.Run(FILE_ERROR_OK
, static_cast<int64
>(position
));
170 void FileImpl::Stat(const StatCallback
& callback
) {
171 if (!file_
.IsValid()) {
172 callback
.Run(GetError(file_
), nullptr);
176 base::File::Info info
;
177 if (!file_
.GetInfo(&info
)) {
178 callback
.Run(FILE_ERROR_FAILED
, nullptr);
182 callback
.Run(FILE_ERROR_OK
, MakeFileInformation(info
).Pass());
185 void FileImpl::Truncate(int64_t size
, const TruncateCallback
& callback
) {
186 if (!file_
.IsValid()) {
187 callback
.Run(GetError(file_
));
191 callback
.Run(FILE_ERROR_INVALID_OPERATION
);
194 if (FileError error
= IsOffsetValid(size
)) {
199 if (!file_
.SetLength(size
)) {
200 callback
.Run(FILE_ERROR_NOT_FOUND
);
204 callback
.Run(FILE_ERROR_OK
);
207 void FileImpl::Touch(TimespecOrNowPtr atime
,
208 TimespecOrNowPtr mtime
,
209 const TouchCallback
& callback
) {
210 if (!file_
.IsValid()) {
211 callback
.Run(GetError(file_
));
215 base::Time base_atime
= Time::Now();
217 base::File::Info info
;
218 if (!file_
.GetInfo(&info
)) {
219 callback
.Run(FILE_ERROR_FAILED
);
223 base_atime
= info
.last_accessed
;
224 } else if (!atime
->now
) {
225 base_atime
= Time::FromDoubleT(atime
->seconds
);
228 base::Time base_mtime
= Time::Now();
230 base::File::Info info
;
231 if (!file_
.GetInfo(&info
)) {
232 callback
.Run(FILE_ERROR_FAILED
);
236 base_mtime
= info
.last_modified
;
237 } else if (!mtime
->now
) {
238 base_mtime
= Time::FromDoubleT(mtime
->seconds
);
241 file_
.SetTimes(base_atime
, base_mtime
);
242 callback
.Run(FILE_ERROR_OK
);
245 void FileImpl::Dup(mojo::InterfaceRequest
<File
> file
,
246 const DupCallback
& callback
) {
247 if (!file_
.IsValid()) {
248 callback
.Run(GetError(file_
));
252 base::File new_file
= file_
.Duplicate();
253 if (!new_file
.IsValid()) {
254 callback
.Run(GetError(new_file
));
258 if (file
.is_pending())
259 new FileImpl(file
.Pass(), new_file
.Pass());
260 callback
.Run(FILE_ERROR_OK
);
263 void FileImpl::Flush(const FlushCallback
& callback
) {
264 if (!file_
.IsValid()) {
265 callback
.Run(GetError(file_
));
269 bool ret
= file_
.Flush();
270 callback
.Run(ret
? FILE_ERROR_OK
: FILE_ERROR_FAILED
);
273 void FileImpl::AsHandle(const AsHandleCallback
& callback
) {
274 if (!file_
.IsValid()) {
275 callback
.Run(GetError(file_
), ScopedHandle());
279 base::File new_file
= file_
.Duplicate();
280 if (!new_file
.IsValid()) {
281 callback
.Run(GetError(new_file
), ScopedHandle());
285 base::File::Info info
;
286 if (!new_file
.GetInfo(&info
)) {
287 callback
.Run(FILE_ERROR_FAILED
, ScopedHandle());
291 // Perform one additional check right before we send the file's file
292 // descriptor over mojo. This is theoretically redundant, but given that
293 // passing a file descriptor to a directory is a sandbox escape on Windows,
294 // we should be absolutely paranoid.
295 if (info
.is_directory
) {
296 callback
.Run(FILE_ERROR_NOT_A_FILE
, ScopedHandle());
300 MojoHandle mojo_handle
;
301 MojoResult create_result
= MojoCreatePlatformHandleWrapper(
302 new_file
.TakePlatformFile(), &mojo_handle
);
303 if (create_result
!= MOJO_RESULT_OK
) {
304 callback
.Run(FILE_ERROR_FAILED
, ScopedHandle());
308 callback
.Run(FILE_ERROR_OK
, ScopedHandle(mojo::Handle(mojo_handle
)).Pass());
311 } // namespace filesystem