Don't show supervised user as "already on this device" while they're being imported.
[chromium-blink-merge.git] / components / filesystem / file_impl.cc
blobbb524e6ca32f9383c0d38363230b885805c4ab02
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"
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <sys/stat.h>
10 #include <sys/types.h>
11 #include <unistd.h>
13 #include <limits>
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);
39 return;
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.
48 int error = errno;
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));
54 return;
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,
62 int64_t offset,
63 Whence whence,
64 const ReadCallback& callback) {
65 if (!file_fd_.is_valid()) {
66 callback.Run(ERROR_CLOSED, mojo::Array<uint8_t>());
67 return;
69 if (num_bytes_to_read > kMaxReadSize) {
70 callback.Run(ERROR_OUT_OF_RANGE, mojo::Array<uint8_t>());
71 return;
73 if (Error error = IsOffsetValid(offset)) {
74 callback.Run(error, mojo::Array<uint8_t>());
75 return;
77 if (Error error = IsWhenceValid(whence)) {
78 callback.Run(error, mojo::Array<uint8_t>());
79 return;
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>());
92 return;
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>());
101 return;
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,
111 int64_t offset,
112 Whence whence,
113 const WriteCallback& callback) {
114 DCHECK(!bytes_to_write.is_null());
116 if (!file_fd_.is_valid()) {
117 callback.Run(ERROR_CLOSED, 0);
118 return;
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);
125 return;
127 if (Error error = IsOffsetValid(offset)) {
128 callback.Run(error, 0);
129 return;
131 if (Error error = IsWhenceValid(whence)) {
132 callback.Run(error, 0);
133 return;
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
141 // |pwrite()|.
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);
147 return;
151 const void* buf =
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);
157 return;
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,
166 int64_t offset,
167 Whence whence,
168 int64_t num_bytes_to_read,
169 const ReadToStreamCallback& callback) {
170 if (!file_fd_.is_valid()) {
171 callback.Run(ERROR_CLOSED);
172 return;
174 if (Error error = IsOffsetValid(offset)) {
175 callback.Run(error);
176 return;
178 if (Error error = IsWhenceValid(whence)) {
179 callback.Run(error);
180 return;
183 // TODO(vtl): FIXME soon
184 NOTIMPLEMENTED();
185 callback.Run(ERROR_UNIMPLEMENTED);
188 void FileImpl::WriteFromStream(mojo::ScopedDataPipeConsumerHandle sink,
189 int64_t offset,
190 Whence whence,
191 const WriteFromStreamCallback& callback) {
192 if (!file_fd_.is_valid()) {
193 callback.Run(ERROR_CLOSED);
194 return;
196 if (Error error = IsOffsetValid(offset)) {
197 callback.Run(error);
198 return;
200 if (Error error = IsWhenceValid(whence)) {
201 callback.Run(error);
202 return;
205 // TODO(vtl): FIXME soon
206 NOTIMPLEMENTED();
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,
215 Whence whence,
216 const SeekCallback& callback) {
217 if (!file_fd_.is_valid()) {
218 callback.Run(ERROR_CLOSED, 0);
219 return;
221 if (Error error = IsOffsetValid(offset)) {
222 callback.Run(error, 0);
223 return;
225 if (Error error = IsWhenceValid(whence)) {
226 callback.Run(error, 0);
227 return;
230 off_t position = lseek(file_fd_.get(), static_cast<off_t>(offset),
231 WhenceToStandardWhence(whence));
232 if (position < 0) {
233 callback.Run(ErrnoToError(errno), 0);
234 return;
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);
243 return;
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);
251 return;
253 if (size < 0) {
254 callback.Run(ERROR_INVALID_ARGUMENT);
255 return;
257 if (Error error = IsOffsetValid(size)) {
258 callback.Run(error);
259 return;
262 if (ftruncate(file_fd_.get(), static_cast<off_t>(size)) != 0) {
263 callback.Run(ErrnoToError(errno));
264 return;
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);
275 return;
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);
284 return;
287 base::ScopedFD file_fd(dup(file_fd_.get()));
288 if (!file_fd.is_valid()) {
289 callback.Run(ErrnoToError(errno));
290 return;
293 new FileImpl(file.Pass(), file_fd.Pass());
294 callback.Run(ERROR_OK);
297 void FileImpl::Reopen(mojo::InterfaceRequest<File> file,
298 uint32_t open_flags,
299 const ReopenCallback& callback) {
300 if (!file_fd_.is_valid()) {
301 callback.Run(ERROR_CLOSED);
302 return;
305 // TODO(vtl): FIXME soon
306 NOTIMPLEMENTED();
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());
313 return;
316 // TODO(vtl): FIXME soon
317 NOTIMPLEMENTED();
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>());
326 return;
329 // TODO(vtl): The "correct" error code should be one that can be translated to
330 // ENOTTY!
331 callback.Run(ERROR_UNAVAILABLE, mojo::Array<uint32_t>());
334 } // namespace filesystem