| OLD | NEW | 
|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 #include "components/filesystem/file_impl.h" | 5 #include "components/filesystem/file_impl.h" | 
| 6 | 6 | 
| 7 #include <errno.h> |  | 
| 8 #include <fcntl.h> |  | 
| 9 #include <sys/stat.h> |  | 
| 10 #include <sys/types.h> |  | 
| 11 #include <unistd.h> |  | 
| 12 |  | 
| 13 #include <limits> | 7 #include <limits> | 
| 14 | 8 | 
|  | 9 #include "base/files/file_path.h" | 
| 15 #include "base/files/scoped_file.h" | 10 #include "base/files/scoped_file.h" | 
| 16 #include "base/logging.h" | 11 #include "base/logging.h" | 
| 17 #include "base/posix/eintr_wrapper.h" |  | 
| 18 #include "components/filesystem/shared_impl.h" |  | 
| 19 #include "components/filesystem/util.h" | 12 #include "components/filesystem/util.h" | 
| 20 | 13 | 
| 21 static_assert(sizeof(off_t) <= sizeof(int64_t), "off_t too big"); | 14 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"); | 15 static_assert(sizeof(size_t) >= sizeof(uint32_t), "size_t too small"); | 
| 23 | 16 | 
|  | 17 using base::Time; | 
|  | 18 | 
| 24 namespace filesystem { | 19 namespace filesystem { | 
| 25 | 20 | 
| 26 const size_t kMaxReadSize = 1 * 1024 * 1024;  // 1 MB. | 21 const size_t kMaxReadSize = 1 * 1024 * 1024;  // 1 MB. | 
| 27 | 22 | 
| 28 FileImpl::FileImpl(mojo::InterfaceRequest<File> request, base::ScopedFD file_fd) | 23 FileImpl::FileImpl(mojo::InterfaceRequest<File> request, | 
| 29     : binding_(this, request.Pass()), file_fd_(file_fd.Pass()) { | 24                    const base::FilePath& path, | 
| 30   DCHECK(file_fd_.is_valid()); | 25                    uint32 flags) | 
|  | 26     : binding_(this, request.Pass()), file_(path, flags) { | 
|  | 27   DCHECK(file_.IsValid()); | 
|  | 28 } | 
|  | 29 | 
|  | 30 FileImpl::FileImpl(mojo::InterfaceRequest<File> request, base::File file) | 
|  | 31     : binding_(this, request.Pass()), file_(file.Pass()) { | 
|  | 32   DCHECK(file_.IsValid()); | 
| 31 } | 33 } | 
| 32 | 34 | 
| 33 FileImpl::~FileImpl() { | 35 FileImpl::~FileImpl() { | 
| 34 } | 36 } | 
| 35 | 37 | 
| 36 void FileImpl::Close(const CloseCallback& callback) { | 38 void FileImpl::Close(const CloseCallback& callback) { | 
| 37   if (!file_fd_.is_valid()) { | 39   if (!file_.IsValid()) { | 
| 38     callback.Run(ERROR_CLOSED); | 40     callback.Run(GetError(file_)); | 
| 39     return; |  | 
| 40   } |  | 
| 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; | 41     return; | 
| 55   } | 42   } | 
| 56 | 43 | 
|  | 44   file_.Close(); | 
| 57   callback.Run(ERROR_OK); | 45   callback.Run(ERROR_OK); | 
| 58 } | 46 } | 
| 59 | 47 | 
| 60 // TODO(vtl): Move the implementation to a thread pool. | 48 // TODO(vtl): Move the implementation to a thread pool. | 
| 61 void FileImpl::Read(uint32_t num_bytes_to_read, | 49 void FileImpl::Read(uint32_t num_bytes_to_read, | 
| 62                     int64_t offset, | 50                     int64_t offset, | 
| 63                     Whence whence, | 51                     Whence whence, | 
| 64                     const ReadCallback& callback) { | 52                     const ReadCallback& callback) { | 
| 65   if (!file_fd_.is_valid()) { | 53   if (!file_.IsValid()) { | 
| 66     callback.Run(ERROR_CLOSED, mojo::Array<uint8_t>()); | 54     callback.Run(GetError(file_), mojo::Array<uint8_t>()); | 
| 67     return; | 55     return; | 
| 68   } | 56   } | 
| 69   if (num_bytes_to_read > kMaxReadSize) { | 57   if (num_bytes_to_read > kMaxReadSize) { | 
| 70     callback.Run(ERROR_OUT_OF_RANGE, mojo::Array<uint8_t>()); | 58     callback.Run(ERROR_INVALID_OPERATION, mojo::Array<uint8_t>()); | 
| 71     return; | 59     return; | 
| 72   } | 60   } | 
| 73   if (Error error = IsOffsetValid(offset)) { | 61   if (Error error = IsOffsetValid(offset)) { | 
| 74     callback.Run(error, mojo::Array<uint8_t>()); | 62     callback.Run(error, mojo::Array<uint8_t>()); | 
| 75     return; | 63     return; | 
| 76   } | 64   } | 
| 77   if (Error error = IsWhenceValid(whence)) { | 65   if (Error error = IsWhenceValid(whence)) { | 
| 78     callback.Run(error, mojo::Array<uint8_t>()); | 66     callback.Run(error, mojo::Array<uint8_t>()); | 
| 79     return; | 67     return; | 
| 80   } | 68   } | 
| 81 | 69 | 
| 82   if (offset != 0 || whence != WHENCE_FROM_CURRENT) { | 70   if (file_.Seek(static_cast<base::File::Whence>(whence), offset) == -1) { | 
| 83     // TODO(vtl): Use |pread()| below in the |WHENCE_FROM_START| case. This | 71     callback.Run(ERROR_FAILED, mojo::Array<uint8_t>()); | 
| 84     // implementation is obviously not atomic. (If someone seeks simultaneously, | 72     return; | 
| 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; |  | 
| 93     } |  | 
| 94   } | 73   } | 
| 95 | 74 | 
| 96   mojo::Array<uint8_t> bytes_read(num_bytes_to_read); | 75   mojo::Array<uint8_t> bytes_read(num_bytes_to_read); | 
| 97   ssize_t num_bytes_read = HANDLE_EINTR( | 76   int num_bytes_read = file_.ReadAtCurrentPos( | 
| 98       read(file_fd_.get(), &bytes_read.front(), num_bytes_to_read)); | 77       reinterpret_cast<char*>(&bytes_read.front()), num_bytes_to_read); | 
| 99   if (num_bytes_read < 0) { | 78   if (num_bytes_read < 0) { | 
| 100     callback.Run(ErrnoToError(errno), mojo::Array<uint8_t>()); | 79     callback.Run(ERROR_FAILED, mojo::Array<uint8_t>()); | 
| 101     return; | 80     return; | 
| 102   } | 81   } | 
| 103 | 82 | 
| 104   DCHECK_LE(static_cast<size_t>(num_bytes_read), num_bytes_to_read); | 83   DCHECK_LE(static_cast<size_t>(num_bytes_read), num_bytes_to_read); | 
| 105   bytes_read.resize(static_cast<size_t>(num_bytes_read)); | 84   bytes_read.resize(static_cast<size_t>(num_bytes_read)); | 
| 106   callback.Run(ERROR_OK, bytes_read.Pass()); | 85   callback.Run(ERROR_OK, bytes_read.Pass()); | 
| 107 } | 86 } | 
| 108 | 87 | 
| 109 // TODO(vtl): Move the implementation to a thread pool. | 88 // TODO(vtl): Move the implementation to a thread pool. | 
| 110 void FileImpl::Write(mojo::Array<uint8_t> bytes_to_write, | 89 void FileImpl::Write(mojo::Array<uint8_t> bytes_to_write, | 
| 111                      int64_t offset, | 90                      int64_t offset, | 
| 112                      Whence whence, | 91                      Whence whence, | 
| 113                      const WriteCallback& callback) { | 92                      const WriteCallback& callback) { | 
| 114   DCHECK(!bytes_to_write.is_null()); | 93   DCHECK(!bytes_to_write.is_null()); | 
| 115 | 94   if (!file_.IsValid()) { | 
| 116   if (!file_fd_.is_valid()) { | 95     callback.Run(GetError(file_), 0); | 
| 117     callback.Run(ERROR_CLOSED, 0); |  | 
| 118     return; | 96     return; | 
| 119   } | 97   } | 
| 120   // Who knows what |write()| would return if the size is that big (and it | 98   // Who knows what |write()| would return if the size is that big (and it | 
| 121   // actually wrote that much). | 99   // actually wrote that much). | 
| 122   if (bytes_to_write.size() > | 100   if (bytes_to_write.size() > | 
| 123       static_cast<size_t>(std::numeric_limits<ssize_t>::max())) { | 101       static_cast<size_t>(std::numeric_limits<ssize_t>::max())) { | 
| 124     callback.Run(ERROR_OUT_OF_RANGE, 0); | 102     callback.Run(ERROR_INVALID_OPERATION, 0); | 
| 125     return; | 103     return; | 
| 126   } | 104   } | 
| 127   if (Error error = IsOffsetValid(offset)) { | 105   if (Error error = IsOffsetValid(offset)) { | 
| 128     callback.Run(error, 0); | 106     callback.Run(error, 0); | 
| 129     return; | 107     return; | 
| 130   } | 108   } | 
| 131   if (Error error = IsWhenceValid(whence)) { | 109   if (Error error = IsWhenceValid(whence)) { | 
| 132     callback.Run(error, 0); | 110     callback.Run(error, 0); | 
| 133     return; | 111     return; | 
| 134   } | 112   } | 
| 135 | 113 | 
| 136   if (offset != 0 || whence != WHENCE_FROM_CURRENT) { | 114   if (file_.Seek(static_cast<base::File::Whence>(whence), offset) == -1) { | 
| 137     // TODO(vtl): Use |pwrite()| below in the |WHENCE_FROM_START| case. This | 115     callback.Run(ERROR_FAILED, 0); | 
| 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; |  | 
| 148     } |  | 
| 149   } |  | 
| 150 |  | 
| 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; | 116     return; | 
| 158   } | 117   } | 
| 159 | 118 | 
|  | 119   const char* buf = (bytes_to_write.size() > 0) | 
|  | 120                         ? reinterpret_cast<char*>(&bytes_to_write.front()) | 
|  | 121                         : nullptr; | 
|  | 122   int num_bytes_written = file_.WriteAtCurrentPos(buf, bytes_to_write.size()); | 
|  | 123   if (num_bytes_written < 0) { | 
|  | 124     callback.Run(ERROR_FAILED, 0); | 
|  | 125     return; | 
|  | 126   } | 
|  | 127 | 
| 160   DCHECK_LE(static_cast<size_t>(num_bytes_written), | 128   DCHECK_LE(static_cast<size_t>(num_bytes_written), | 
| 161             std::numeric_limits<uint32_t>::max()); | 129             std::numeric_limits<uint32_t>::max()); | 
| 162   callback.Run(ERROR_OK, static_cast<uint32_t>(num_bytes_written)); | 130   callback.Run(ERROR_OK, static_cast<uint32_t>(num_bytes_written)); | 
| 163 } | 131 } | 
| 164 | 132 | 
| 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; |  | 
| 173   } |  | 
| 174   if (Error error = IsOffsetValid(offset)) { |  | 
| 175     callback.Run(error); |  | 
| 176     return; |  | 
| 177   } |  | 
| 178   if (Error error = IsWhenceValid(whence)) { |  | 
| 179     callback.Run(error); |  | 
| 180     return; |  | 
| 181   } |  | 
| 182 |  | 
| 183   // TODO(vtl): FIXME soon |  | 
| 184   NOTIMPLEMENTED(); |  | 
| 185   callback.Run(ERROR_UNIMPLEMENTED); |  | 
| 186 } |  | 
| 187 |  | 
| 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; |  | 
| 195   } |  | 
| 196   if (Error error = IsOffsetValid(offset)) { |  | 
| 197     callback.Run(error); |  | 
| 198     return; |  | 
| 199   } |  | 
| 200   if (Error error = IsWhenceValid(whence)) { |  | 
| 201     callback.Run(error); |  | 
| 202     return; |  | 
| 203   } |  | 
| 204 |  | 
| 205   // TODO(vtl): FIXME soon |  | 
| 206   NOTIMPLEMENTED(); |  | 
| 207   callback.Run(ERROR_UNIMPLEMENTED); |  | 
| 208 } |  | 
| 209 |  | 
| 210 void FileImpl::Tell(const TellCallback& callback) { | 133 void FileImpl::Tell(const TellCallback& callback) { | 
| 211   Seek(0, WHENCE_FROM_CURRENT, callback); | 134   Seek(0, WHENCE_FROM_CURRENT, callback); | 
| 212 } | 135 } | 
| 213 | 136 | 
| 214 void FileImpl::Seek(int64_t offset, | 137 void FileImpl::Seek(int64_t offset, | 
| 215                     Whence whence, | 138                     Whence whence, | 
| 216                     const SeekCallback& callback) { | 139                     const SeekCallback& callback) { | 
| 217   if (!file_fd_.is_valid()) { | 140   if (!file_.IsValid()) { | 
| 218     callback.Run(ERROR_CLOSED, 0); | 141     callback.Run(GetError(file_), 0); | 
| 219     return; | 142     return; | 
| 220   } | 143   } | 
| 221   if (Error error = IsOffsetValid(offset)) { | 144   if (Error error = IsOffsetValid(offset)) { | 
| 222     callback.Run(error, 0); | 145     callback.Run(error, 0); | 
| 223     return; | 146     return; | 
| 224   } | 147   } | 
| 225   if (Error error = IsWhenceValid(whence)) { | 148   if (Error error = IsWhenceValid(whence)) { | 
| 226     callback.Run(error, 0); | 149     callback.Run(error, 0); | 
| 227     return; | 150     return; | 
| 228   } | 151   } | 
| 229 | 152 | 
| 230   off_t position = lseek(file_fd_.get(), static_cast<off_t>(offset), | 153   int64 position = file_.Seek(static_cast<base::File::Whence>(whence), offset); | 
| 231                          WhenceToStandardWhence(whence)); |  | 
| 232   if (position < 0) { | 154   if (position < 0) { | 
| 233     callback.Run(ErrnoToError(errno), 0); | 155     callback.Run(ERROR_FAILED, 0); | 
| 234     return; | 156     return; | 
| 235   } | 157   } | 
| 236 | 158 | 
| 237   callback.Run(ERROR_OK, static_cast<int64>(position)); | 159   callback.Run(ERROR_OK, static_cast<int64>(position)); | 
| 238 } | 160 } | 
| 239 | 161 | 
| 240 void FileImpl::Stat(const StatCallback& callback) { | 162 void FileImpl::Stat(const StatCallback& callback) { | 
| 241   if (!file_fd_.is_valid()) { | 163   if (!file_.IsValid()) { | 
| 242     callback.Run(ERROR_CLOSED, nullptr); | 164     callback.Run(GetError(file_), nullptr); | 
| 243     return; | 165     return; | 
| 244   } | 166   } | 
| 245   StatFD(file_fd_.get(), FILE_TYPE_REGULAR_FILE, callback); | 167 | 
|  | 168   base::File::Info info; | 
|  | 169   if (!file_.GetInfo(&info)) { | 
|  | 170     callback.Run(ERROR_FAILED, nullptr); | 
|  | 171     return; | 
|  | 172   } | 
|  | 173 | 
|  | 174   callback.Run(ERROR_OK, MakeFileInformation(info).Pass()); | 
| 246 } | 175 } | 
| 247 | 176 | 
| 248 void FileImpl::Truncate(int64_t size, const TruncateCallback& callback) { | 177 void FileImpl::Truncate(int64_t size, const TruncateCallback& callback) { | 
| 249   if (!file_fd_.is_valid()) { | 178   if (!file_.IsValid()) { | 
| 250     callback.Run(ERROR_CLOSED); | 179     callback.Run(GetError(file_)); | 
| 251     return; | 180     return; | 
| 252   } | 181   } | 
| 253   if (size < 0) { | 182   if (size < 0) { | 
| 254     callback.Run(ERROR_INVALID_ARGUMENT); | 183     callback.Run(ERROR_INVALID_OPERATION); | 
| 255     return; | 184     return; | 
| 256   } | 185   } | 
| 257   if (Error error = IsOffsetValid(size)) { | 186   if (Error error = IsOffsetValid(size)) { | 
| 258     callback.Run(error); | 187     callback.Run(error); | 
| 259     return; | 188     return; | 
| 260   } | 189   } | 
| 261 | 190 | 
| 262   if (ftruncate(file_fd_.get(), static_cast<off_t>(size)) != 0) { | 191   if (!file_.SetLength(size)) { | 
| 263     callback.Run(ErrnoToError(errno)); | 192     callback.Run(ERROR_NOT_FOUND); | 
| 264     return; | 193     return; | 
| 265   } | 194   } | 
| 266 | 195 | 
| 267   callback.Run(ERROR_OK); | 196   callback.Run(ERROR_OK); | 
| 268 } | 197 } | 
| 269 | 198 | 
| 270 void FileImpl::Touch(TimespecOrNowPtr atime, | 199 void FileImpl::Touch(TimespecOrNowPtr atime, | 
| 271                      TimespecOrNowPtr mtime, | 200                      TimespecOrNowPtr mtime, | 
| 272                      const TouchCallback& callback) { | 201                      const TouchCallback& callback) { | 
| 273   if (!file_fd_.is_valid()) { | 202   if (!file_.IsValid()) { | 
| 274     callback.Run(ERROR_CLOSED); | 203     callback.Run(GetError(file_)); | 
| 275     return; | 204     return; | 
| 276   } | 205   } | 
| 277   TouchFD(file_fd_.get(), atime.Pass(), mtime.Pass(), callback); | 206 | 
|  | 207   base::Time base_atime = Time::Now(); | 
|  | 208   if (!atime) { | 
|  | 209     base::File::Info info; | 
|  | 210     if (!file_.GetInfo(&info)) { | 
|  | 211       callback.Run(ERROR_FAILED); | 
|  | 212       return; | 
|  | 213     } | 
|  | 214 | 
|  | 215     base_atime = info.last_accessed; | 
|  | 216   } else if (!atime->now) { | 
|  | 217     base_atime = Time::FromDoubleT(atime->seconds); | 
|  | 218   } | 
|  | 219 | 
|  | 220   base::Time base_mtime = Time::Now(); | 
|  | 221   if (!mtime) { | 
|  | 222     base::File::Info info; | 
|  | 223     if (!file_.GetInfo(&info)) { | 
|  | 224       callback.Run(ERROR_FAILED); | 
|  | 225       return; | 
|  | 226     } | 
|  | 227 | 
|  | 228     base_mtime = info.last_modified; | 
|  | 229   } else if (!mtime->now) { | 
|  | 230     base_mtime = Time::FromDoubleT(mtime->seconds); | 
|  | 231   } | 
|  | 232 | 
|  | 233   file_.SetTimes(base_atime, base_mtime); | 
|  | 234   callback.Run(ERROR_OK); | 
| 278 } | 235 } | 
| 279 | 236 | 
| 280 void FileImpl::Dup(mojo::InterfaceRequest<File> file, | 237 void FileImpl::Dup(mojo::InterfaceRequest<File> file, | 
| 281                    const DupCallback& callback) { | 238                    const DupCallback& callback) { | 
| 282   if (!file_fd_.is_valid()) { | 239   if (!file_.IsValid()) { | 
| 283     callback.Run(ERROR_CLOSED); | 240     callback.Run(GetError(file_)); | 
| 284     return; | 241     return; | 
| 285   } | 242   } | 
| 286 | 243 | 
| 287   base::ScopedFD file_fd(dup(file_fd_.get())); | 244   base::File new_file = file_.Duplicate(); | 
| 288   if (!file_fd.is_valid()) { | 245   if (!new_file.IsValid()) { | 
| 289     callback.Run(ErrnoToError(errno)); | 246     callback.Run(GetError(new_file)); | 
| 290     return; | 247     return; | 
| 291   } | 248   } | 
| 292 | 249 | 
| 293   new FileImpl(file.Pass(), file_fd.Pass()); | 250   if (file.is_pending()) | 
|  | 251     new FileImpl(file.Pass(), new_file.Pass()); | 
| 294   callback.Run(ERROR_OK); | 252   callback.Run(ERROR_OK); | 
| 295 } | 253 } | 
| 296 | 254 | 
| 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; |  | 
| 303   } |  | 
| 304 |  | 
| 305   // TODO(vtl): FIXME soon |  | 
| 306   NOTIMPLEMENTED(); |  | 
| 307   callback.Run(ERROR_UNIMPLEMENTED); |  | 
| 308 } |  | 
| 309 |  | 
| 310 void FileImpl::AsBuffer(const AsBufferCallback& callback) { |  | 
| 311   if (!file_fd_.is_valid()) { |  | 
| 312     callback.Run(ERROR_CLOSED, mojo::ScopedSharedBufferHandle()); |  | 
| 313     return; |  | 
| 314   } |  | 
| 315 |  | 
| 316   // TODO(vtl): FIXME soon |  | 
| 317   NOTIMPLEMENTED(); |  | 
| 318   callback.Run(ERROR_UNIMPLEMENTED, mojo::ScopedSharedBufferHandle()); |  | 
| 319 } |  | 
| 320 |  | 
| 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; |  | 
| 327   } |  | 
| 328 |  | 
| 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>()); |  | 
| 332 } |  | 
| 333 |  | 
| 334 }  // namespace filesystem | 255 }  // namespace filesystem | 
| OLD | NEW | 
|---|