blob: fd82669c89ae6fc91f579e658c1832c4d4b3f126 [file] [log] [blame]
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -07001// 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.
4
5#include "apps/moterm/moterm_driver.h"
6
7#include <algorithm>
8#include <limits>
9
10#include "base/logging.h"
Viet-Trung Luu08e339a2015-10-10 01:03:09 -070011#include "mojo/services/files/interfaces/ioctl.mojom.h"
12#include "mojo/services/files/interfaces/ioctl_terminal.mojom.h"
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -070013
Viet-Trung Luu8ae18872015-09-10 18:45:32 -070014// Character constants:
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -070015const uint8_t kEOT = 4;
16const uint8_t kNL = 10;
17const uint8_t kCR = 13;
18const uint8_t kDEL = 127;
19
Viet-Trung Luu8ae18872015-09-10 18:45:32 -070020// Short forms for various counts/indices used for the get/set settings ioctls.
21const size_t kBaseFieldCount = mojo::files::kIoctlTerminalTermiosBaseFieldCount;
22const size_t kTotalFieldCount =
23 kBaseFieldCount + mojo::files::kIoctlTerminalTermiosCtrlCharCount;
24const size_t kIFlagIdx = mojo::files::kIoctlTerminalTermiosIFlagIndex;
25const size_t kOFlagIdx = mojo::files::kIoctlTerminalTermiosOFlagIndex;
26const size_t kLFlagIdx = mojo::files::kIoctlTerminalTermiosLFlagIndex;
27const size_t kVEraseIdx = mojo::files::kIoctlTerminalTermiosCtrlCharVERASEIndex;
28const size_t kVEOFIdx = mojo::files::kIoctlTerminalTermiosCtrlCharVERASEIndex;
29
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -070030MotermDriver::PendingRead::PendingRead(uint32_t num_bytes,
31 const ReadCallback& callback)
32 : num_bytes(num_bytes), callback(callback) {
33}
34
35MotermDriver::PendingRead::~PendingRead() {
36}
37
38// static
39base::WeakPtr<MotermDriver> MotermDriver::Create(
40 Client* client,
41 mojo::InterfaceRequest<mojo::files::File> request) {
42 return (new MotermDriver(client, request.Pass()))->weak_factory_.GetWeakPtr();
43}
44
45void MotermDriver::Detach() {
46 client_ = nullptr;
47 delete this;
48}
49
50void MotermDriver::SendData(const void* bytes, size_t num_bytes) {
51 DCHECK(client_);
52 DCHECK(!is_closed_);
53
54 if (icanon_) {
55 for (size_t i = 0; i < num_bytes; i++)
56 HandleCanonicalModeInput(static_cast<const uint8_t*>(bytes)[i]);
57 } else {
58 // If not in canonical ("cooked") mode, just send data.
59 const uint8_t* b = static_cast<const uint8_t*>(bytes);
60 for (size_t i = 0; i < num_bytes; i++)
61 send_data_queue_.push_back(b[i]);
62
63 CompletePendingReads();
64 }
65}
66
67MotermDriver::MotermDriver(Client* client,
68 mojo::InterfaceRequest<mojo::files::File> request)
69 : client_(client),
70 is_closed_(false),
71 binding_(this, request.Pass()),
72 icanon_(true),
73 icrnl_(true),
74 veof_(kEOT),
75 verase_(kDEL),
76 onlcr_(true),
77 weak_factory_(this) {
78 DCHECK(client_);
79}
80
81MotermDriver::~MotermDriver() {
82 if (client_) {
83 // Invalidate weak pointers now, to make sure the client doesn't call us.
84 weak_factory_.InvalidateWeakPtrs();
85 client_->OnDestroyed();
86 }
87}
88
89void MotermDriver::HandleCanonicalModeInput(uint8_t ch) {
90 DCHECK(icanon_);
91
92 // Translate CR to NL, if appropriate.
93 if (ch == kCR && icrnl_)
94 ch = kNL;
95
96 // Newline.
97 // TODO(vtl): In addition to NL, we're supposed to handle settable EOL and
98 // EOL2.
99 if (ch == kNL) { // Newline.
100 input_line_queue_.push_back(ch);
101 HandleOutput(&ch, 1);
102 FlushInputLine();
103 return;
104 }
105
106 // EOF at beginning of line.
107 if (ch == veof_ && input_line_queue_.empty()) {
108 // Don't add the character. Just flush and nuke ourselves.
109 FlushInputLine();
110 delete this;
111 return;
112 }
113
114 if (ch == verase_) { // Erase (backspace).
115 if (!input_line_queue_.empty()) {
116 // TODO(vtl): This is wrong for utf-8 (see Linux's IUTF8).
117 input_line_queue_.pop_back();
118 HandleOutput(reinterpret_cast<const uint8_t*>("\x08 \x08"), 3);
119 } // Else do nothing.
120 return;
121 }
122
123 // Everything else.
124 // TODO(vtl): Probably want to display control characters as ^X (e.g.).
125 // TODO(vtl): Handle ^W.
126 input_line_queue_.push_back(ch);
127 HandleOutput(&ch, 1);
128}
129
130void MotermDriver::CompletePendingReads() {
131 while (send_data_queue_.size() && pending_read_queue_.size()) {
132 PendingRead pending_read = pending_read_queue_.front();
133 pending_read_queue_.pop_front();
134
135 size_t data_size = std::min(static_cast<size_t>(pending_read.num_bytes),
136 send_data_queue_.size());
James Robinsondc335c02015-10-09 17:23:30 -0700137 auto data = mojo::Array<uint8_t>::New(data_size);
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700138 for (size_t i = 0; i < data_size; i++) {
139 data[i] = send_data_queue_[i];
140 // In canonical mode, each read only gets a single line.
141 if (icanon_ && data[i] == kNL) {
142 data_size = i + 1;
143 data.resize(data_size);
144 break;
145 }
146 }
147 send_data_queue_.erase(send_data_queue_.begin(),
148 send_data_queue_.begin() + data_size);
149
John Grossman87fe5142015-10-02 10:11:43 -0700150 pending_read.callback.Run(mojo::files::Error::OK, data.Pass());
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700151 }
152}
153
154void MotermDriver::FlushInputLine() {
155 for (size_t i = 0; i < input_line_queue_.size(); i++)
156 send_data_queue_.push_back(input_line_queue_[i]);
157 input_line_queue_.clear();
158 CompletePendingReads();
159}
160
161void MotermDriver::HandleOutput(const uint8_t* bytes, size_t num_bytes) {
162 // Can we be smarter and not always copy?
163 std::vector<uint8_t> translated_bytes;
164 translated_bytes.reserve(num_bytes);
165
166 for (size_t i = 0; i < num_bytes; i++) {
167 uint8_t ch = bytes[i];
168 if (ch == kNL && onlcr_) {
169 translated_bytes.push_back(kCR);
170 translated_bytes.push_back(kNL);
171 } else {
172 translated_bytes.push_back(ch);
173 }
174 }
175
176 // TODO(vtl): It seems extremely unlikely that we'd overlow a |uint32_t| here
177 // (but it's theoretically possible). But perhaps we should handle it more
178 // gracefully if it ever comes up.
179 CHECK_LE(translated_bytes.size(), std::numeric_limits<uint32_t>::max());
180 client_->OnDataReceived(translated_bytes.data(),
181 static_cast<uint32_t>(translated_bytes.size()));
182}
183
184void MotermDriver::Close(const CloseCallback& callback) {
185 if (is_closed_) {
John Grossman87fe5142015-10-02 10:11:43 -0700186 callback.Run(mojo::files::Error::CLOSED);
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700187 return;
188 }
189
Viet-Trung Luuc8e6b802015-10-01 14:58:35 -0700190 // TODO(vtl): Call pending read callbacks?
191
Viet-Trung Luub8fecbf2015-09-25 11:11:07 -0700192 is_closed_ = true;
John Grossman87fe5142015-10-02 10:11:43 -0700193 callback.Run(mojo::files::Error::OK);
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700194
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700195 client_->OnClosed();
196}
197
198void MotermDriver::Read(uint32_t num_bytes_to_read,
199 int64_t offset,
200 mojo::files::Whence whence,
201 const ReadCallback& callback) {
202 if (is_closed_) {
Viet-Trung Luu06341ac2015-11-19 13:02:01 -0800203 callback.Run(mojo::files::Error::CLOSED, nullptr);
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700204 return;
205 }
206
John Grossman87fe5142015-10-02 10:11:43 -0700207 if (offset != 0 || whence != mojo::files::Whence::FROM_CURRENT) {
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700208 // TODO(vtl): Is this the "right" behavior?
Viet-Trung Luu06341ac2015-11-19 13:02:01 -0800209 callback.Run(mojo::files::Error::INVALID_ARGUMENT, nullptr);
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700210 return;
211 }
212
213 if (!num_bytes_to_read) {
Viet-Trung Luu06341ac2015-11-19 13:02:01 -0800214 callback.Run(mojo::files::Error::OK, nullptr);
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700215 return;
216 }
217
218 pending_read_queue_.push_back(PendingRead(num_bytes_to_read, callback));
219 CompletePendingReads();
220}
221
222void MotermDriver::Write(mojo::Array<uint8_t> bytes_to_write,
223 int64_t offset,
224 mojo::files::Whence whence,
225 const WriteCallback& callback) {
226 DCHECK(!bytes_to_write.is_null());
227
228 if (is_closed_) {
John Grossman87fe5142015-10-02 10:11:43 -0700229 callback.Run(mojo::files::Error::CLOSED, 0);
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700230 return;
231 }
232
John Grossman87fe5142015-10-02 10:11:43 -0700233 if (offset != 0 || whence != mojo::files::Whence::FROM_CURRENT) {
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700234 // TODO(vtl): Is this the "right" behavior?
John Grossman87fe5142015-10-02 10:11:43 -0700235 callback.Run(mojo::files::Error::INVALID_ARGUMENT, 0);
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700236 return;
237 }
238
239 if (!bytes_to_write.size()) {
John Grossman87fe5142015-10-02 10:11:43 -0700240 callback.Run(mojo::files::Error::OK, 0);
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700241 return;
242 }
243
Viet-Trung Luub8fecbf2015-09-25 11:11:07 -0700244 HandleOutput(&bytes_to_write.front(), bytes_to_write.size());
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700245
246 // TODO(vtl): Is this OK if the client detached (and we're destroyed?).
John Grossman87fe5142015-10-02 10:11:43 -0700247 callback.Run(mojo::files::Error::OK,
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700248 static_cast<uint32_t>(bytes_to_write.size()));
249}
250
251void MotermDriver::ReadToStream(mojo::ScopedDataPipeProducerHandle source,
252 int64_t offset,
253 mojo::files::Whence whence,
254 int64_t num_bytes_to_read,
255 const ReadToStreamCallback& callback) {
256 if (is_closed_) {
John Grossman87fe5142015-10-02 10:11:43 -0700257 callback.Run(mojo::files::Error::CLOSED);
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700258 return;
259 }
260
261 // TODO(vtl)
262 NOTIMPLEMENTED();
John Grossman87fe5142015-10-02 10:11:43 -0700263 callback.Run(mojo::files::Error::UNIMPLEMENTED);
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700264}
265
266void MotermDriver::WriteFromStream(mojo::ScopedDataPipeConsumerHandle sink,
267 int64_t offset,
268 mojo::files::Whence whence,
269 const WriteFromStreamCallback& callback) {
270 if (is_closed_) {
John Grossman87fe5142015-10-02 10:11:43 -0700271 callback.Run(mojo::files::Error::CLOSED);
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700272 return;
273 }
274
275 // TODO(vtl)
276 NOTIMPLEMENTED();
John Grossman87fe5142015-10-02 10:11:43 -0700277 callback.Run(mojo::files::Error::UNIMPLEMENTED);
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700278}
279
280void MotermDriver::Tell(const TellCallback& callback) {
281 if (is_closed_) {
John Grossman87fe5142015-10-02 10:11:43 -0700282 callback.Run(mojo::files::Error::CLOSED, 0);
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700283 return;
284 }
285
286 // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe
287 // unsupported/EINVAL is better.)
John Grossman87fe5142015-10-02 10:11:43 -0700288 callback.Run(mojo::files::Error::UNAVAILABLE, 0);
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700289}
290
291void MotermDriver::Seek(int64_t offset,
292 mojo::files::Whence whence,
293 const SeekCallback& callback) {
294 if (is_closed_) {
John Grossman87fe5142015-10-02 10:11:43 -0700295 callback.Run(mojo::files::Error::CLOSED, 0);
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700296 return;
297 }
298
299 // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe
300 // unsupported/EINVAL is better.)
John Grossman87fe5142015-10-02 10:11:43 -0700301 callback.Run(mojo::files::Error::UNAVAILABLE, 0);
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700302}
303
304void MotermDriver::Stat(const StatCallback& callback) {
305 if (is_closed_) {
John Grossman87fe5142015-10-02 10:11:43 -0700306 callback.Run(mojo::files::Error::CLOSED, nullptr);
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700307 return;
308 }
309
310 // TODO(vtl)
311 NOTIMPLEMENTED();
John Grossman87fe5142015-10-02 10:11:43 -0700312 callback.Run(mojo::files::Error::UNIMPLEMENTED, nullptr);
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700313}
314
315void MotermDriver::Truncate(int64_t size, const TruncateCallback& callback) {
316 if (is_closed_) {
John Grossman87fe5142015-10-02 10:11:43 -0700317 callback.Run(mojo::files::Error::CLOSED);
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700318 return;
319 }
320
321 // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe
322 // unsupported/EINVAL is better.)
John Grossman87fe5142015-10-02 10:11:43 -0700323 callback.Run(mojo::files::Error::UNAVAILABLE);
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700324}
325
326void MotermDriver::Touch(mojo::files::TimespecOrNowPtr atime,
327 mojo::files::TimespecOrNowPtr mtime,
328 const TouchCallback& callback) {
329 if (is_closed_) {
John Grossman87fe5142015-10-02 10:11:43 -0700330 callback.Run(mojo::files::Error::CLOSED);
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700331 return;
332 }
333
334 // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe
335 // unsupported/EINVAL is better.)
John Grossman87fe5142015-10-02 10:11:43 -0700336 callback.Run(mojo::files::Error::UNAVAILABLE);
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700337}
338
339void MotermDriver::Dup(mojo::InterfaceRequest<mojo::files::File> file,
340 const DupCallback& callback) {
341 if (is_closed_) {
John Grossman87fe5142015-10-02 10:11:43 -0700342 callback.Run(mojo::files::Error::CLOSED);
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700343 return;
344 }
345
346 // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe
347 // unsupported/EINVAL is better.)
John Grossman87fe5142015-10-02 10:11:43 -0700348 callback.Run(mojo::files::Error::UNAVAILABLE);
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700349}
350
351void MotermDriver::Reopen(mojo::InterfaceRequest<mojo::files::File> file,
352 uint32_t open_flags,
353 const ReopenCallback& callback) {
354 if (is_closed_) {
John Grossman87fe5142015-10-02 10:11:43 -0700355 callback.Run(mojo::files::Error::CLOSED);
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700356 return;
357 }
358
359 // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe
360 // unsupported/EINVAL is better.)
John Grossman87fe5142015-10-02 10:11:43 -0700361 callback.Run(mojo::files::Error::UNAVAILABLE);
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700362}
363
364void MotermDriver::AsBuffer(const AsBufferCallback& callback) {
365 if (is_closed_) {
John Grossman87fe5142015-10-02 10:11:43 -0700366 callback.Run(mojo::files::Error::CLOSED, mojo::ScopedSharedBufferHandle());
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700367 return;
368 }
369
370 // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe
371 // unsupported/EINVAL is better.)
John Grossman87fe5142015-10-02 10:11:43 -0700372 callback.Run(mojo::files::Error::UNAVAILABLE,
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700373 mojo::ScopedSharedBufferHandle());
374}
375
376void MotermDriver::Ioctl(uint32_t request,
377 mojo::Array<uint32_t> in_values,
378 const IoctlCallback& callback) {
379 if (is_closed_) {
Viet-Trung Luu06341ac2015-11-19 13:02:01 -0800380 callback.Run(mojo::files::Error::CLOSED, nullptr);
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700381 return;
382 }
383
Viet-Trung Luu8ae18872015-09-10 18:45:32 -0700384 if (request != mojo::files::kIoctlTerminal) {
Viet-Trung Luu06341ac2015-11-19 13:02:01 -0800385 callback.Run(mojo::files::Error::UNIMPLEMENTED, nullptr);
Viet-Trung Luu8ae18872015-09-10 18:45:32 -0700386 return;
387 }
388
389 // "Is TTY?" Yes.
390 if (!in_values || !in_values.size()) {
Viet-Trung Luu06341ac2015-11-19 13:02:01 -0800391 callback.Run(mojo::files::Error::OK, nullptr);
Viet-Trung Luu8ae18872015-09-10 18:45:32 -0700392 return;
393 }
394
395 switch (in_values[0]) {
396 case mojo::files::kIoctlTerminalGetSettings:
397 IoctlGetSettings(in_values.Pass(), callback);
398 return;
399 case mojo::files::kIoctlTerminalSetSettings:
400 IoctlSetSettings(in_values.Pass(), callback);
401 return;
402 case mojo::files::kIoctlTerminalGetWindowSize:
403 case mojo::files::kIoctlTerminalSetWindowSize:
404 NOTIMPLEMENTED();
Viet-Trung Luu06341ac2015-11-19 13:02:01 -0800405 callback.Run(mojo::files::Error::UNIMPLEMENTED, nullptr);
Viet-Trung Luu8ae18872015-09-10 18:45:32 -0700406 return;
407 default:
Viet-Trung Luu06341ac2015-11-19 13:02:01 -0800408 callback.Run(mojo::files::Error::UNIMPLEMENTED, nullptr);
Viet-Trung Luu8ae18872015-09-10 18:45:32 -0700409 return;
410 }
411}
412
413void MotermDriver::IoctlGetSettings(mojo::Array<uint32_t> in_values,
414 const IoctlCallback& callback) {
415 if (in_values.size() != 1u) {
Viet-Trung Luu06341ac2015-11-19 13:02:01 -0800416 callback.Run(mojo::files::Error::INVALID_ARGUMENT, nullptr);
Viet-Trung Luu8ae18872015-09-10 18:45:32 -0700417 return;
418 }
419
420 auto out_values = mojo::Array<uint32_t>::New(kTotalFieldCount);
421
422 // TODO(vtl): Add support for various things. Also, some values should be
423 // hard-coded.
424
425 // iflag:
426 if (icrnl_)
427 out_values[kIFlagIdx] |= mojo::files::kIoctlTerminalTermiosIFlagICRNL;
428
429 // oflag:
430 if (onlcr_)
431 out_values[kOFlagIdx] |= mojo::files::kIoctlTerminalTermiosOFlagONLCR;
432
433 // lflag:
434 if (icanon_)
435 out_values[kLFlagIdx] |= mojo::files::kIoctlTerminalTermiosLFlagICANON;
436
437 // cc:
438 out_values[kVEraseIdx] = verase_;
439 out_values[kVEOFIdx] = veof_;
440
John Grossman87fe5142015-10-02 10:11:43 -0700441 callback.Run(mojo::files::Error::OK, out_values.Pass());
Viet-Trung Luu8ae18872015-09-10 18:45:32 -0700442}
443
444void MotermDriver::IoctlSetSettings(mojo::Array<uint32_t> in_values,
445 const IoctlCallback& callback) {
Viet-Trung Luu06341ac2015-11-19 13:02:01 -0800446 callback.Run(IoctlSetSettingsHelper(in_values.Pass()), nullptr);
Viet-Trung Luu8ae18872015-09-10 18:45:32 -0700447}
448
449mojo::files::Error MotermDriver::IoctlSetSettingsHelper(
450 mojo::Array<uint32_t> in_values) {
451 // Note: "termios" offsets (and sizes) are increased by 1, to accomodate the
452 // subrequest at index 0.
453
454 // The "cc" values are optional.
455 if (in_values.size() < 1 + kBaseFieldCount)
John Grossman87fe5142015-10-02 10:11:43 -0700456 return mojo::files::Error::INVALID_ARGUMENT;
Viet-Trung Luu8ae18872015-09-10 18:45:32 -0700457
458 // TODO(vtl): Add support for various things. Also, some values can't be
459 // changed.
460
461 // iflag:
462 icrnl_ = !!(in_values[1 + kIFlagIdx] &
463 mojo::files::kIoctlTerminalTermiosIFlagICRNL);
464
465 // oflag:
466 onlcr_ = !!(in_values[1 + kOFlagIdx] &
467 mojo::files::kIoctlTerminalTermiosOFlagONLCR);
468
469 // lflag:
470 icanon_ = !!(in_values[1 + kLFlagIdx] &
471 mojo::files::kIoctlTerminalTermiosLFlagICANON);
472
473 // TODO(vtl): Check that ispeed and ospeed are not set?
474
475 // cc:
476 if (1 + kVEraseIdx < in_values.size()) {
477 uint32_t value = in_values[1 + kVEraseIdx];
478 if (value > std::numeric_limits<uint8_t>::max())
John Grossman87fe5142015-10-02 10:11:43 -0700479 return mojo::files::Error::INVALID_ARGUMENT;
Viet-Trung Luu8ae18872015-09-10 18:45:32 -0700480 verase_ = static_cast<uint8_t>(value);
481 }
482 if (1 + kVEOFIdx < in_values.size()) {
483 uint32_t value = in_values[1 + kVEOFIdx];
484 if (value > std::numeric_limits<uint8_t>::max())
John Grossman87fe5142015-10-02 10:11:43 -0700485 return mojo::files::Error::INVALID_ARGUMENT;
Viet-Trung Luu8ae18872015-09-10 18:45:32 -0700486 veof_ = static_cast<uint8_t>(value);
487 }
488
John Grossman87fe5142015-10-02 10:11:43 -0700489 return mojo::files::Error::OK;
Viet-Trung Luuc0b03df2015-05-11 13:51:55 -0700490}