Skip to content

Commit

Permalink
Add file modes for opening a file write only
Browse files Browse the repository at this point in the history
Added two new file modes WRITE_ONLY and WRITE_ONLY_APPEND. The current
file modes WRITE and WRITE_APPEND are kept as the read/write mode with
no change. Kept the same overall handling of create/truncate/append
semantics for the new file modes.

R=ager@google.com, ahe@google.com
BUG=

Review URL: https://codereview.chromium.org//1193653002.
  • Loading branch information
sgjesse committed Jun 18, 2015
1 parent 9fb192b commit eaeecf2
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 19 deletions.
10 changes: 9 additions & 1 deletion runtime/bin/file.cc
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,21 @@ bool File::WriteFully(const void* buffer, int64_t num_bytes) {
File::FileOpenMode File::DartModeToFileMode(DartFileOpenMode mode) {
ASSERT(mode == File::kDartRead ||
mode == File::kDartWrite ||
mode == File::kDartAppend);
mode == File::kDartAppend ||
mode == File::kDartWriteOnly ||
mode == File::kDartWriteOnlyAppend);
if (mode == File::kDartWrite) {
return File::kWriteTruncate;
}
if (mode == File::kDartAppend) {
return File::kWrite;
}
if (mode == File::kDartWriteOnly) {
return File::kWriteOnlyTruncate;
}
if (mode == File::kDartWriteOnlyAppend) {
return File::kWriteOnly;
}
return File::kRead;
}

Expand Down
11 changes: 8 additions & 3 deletions runtime/bin/file.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,20 @@ class File {
kRead = 0,
kWrite = 1,
kTruncate = 1 << 2,
kWriteTruncate = kWrite | kTruncate
kWriteOnly = 1 << 3,
kWriteTruncate = kWrite | kTruncate,
kWriteOnlyTruncate = kWriteOnly | kTruncate
};

// These values have to be kept in sync with the mode values of
// FileMode.READ, FileMode.WRITE and FileMode.APPEND in file.dart.
// FileMode.READ, FileMode.WRITE, FileMode.APPEND,
// FileMode.WRITE_ONLY and FileMode.WRITE_ONLY_APPEND in file.dart.
enum DartFileOpenMode {
kDartRead = 0,
kDartWrite = 1,
kDartAppend = 2
kDartAppend = 2,
kDartWriteOnly = 3,
kDartWriteOnlyAppend = 4
};

enum Type {
Expand Down
8 changes: 7 additions & 1 deletion runtime/bin/file_android.cc
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,13 @@ File* File::Open(const char* name, FileOpenMode mode) {
}
int flags = O_RDONLY;
if ((mode & kWrite) != 0) {
ASSERT((mode & kWriteOnly) == 0);
flags = (O_RDWR | O_CREAT);
}
if ((mode & kWriteOnly) != 0) {
ASSERT((mode & kWrite) == 0);
flags = (O_WRONLY | O_CREAT);
}
if ((mode & kTruncate) != 0) {
flags = flags | O_TRUNC;
}
Expand All @@ -168,7 +173,8 @@ File* File::Open(const char* name, FileOpenMode mode) {
if (fd < 0) {
return NULL;
}
if (((mode & kWrite) != 0) && ((mode & kTruncate) == 0)) {
if ((((mode & kWrite) != 0) && ((mode & kTruncate) == 0)) ||
(((mode & kWriteOnly) != 0) && ((mode & kTruncate) == 0))) {
int64_t position = lseek64(fd, 0, SEEK_END);
if (position < 0) {
return NULL;
Expand Down
8 changes: 7 additions & 1 deletion runtime/bin/file_linux.cc
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,13 @@ File* File::Open(const char* name, FileOpenMode mode) {
}
int flags = O_RDONLY;
if ((mode & kWrite) != 0) {
ASSERT((mode & kWriteOnly) == 0);
flags = (O_RDWR | O_CREAT);
}
if ((mode & kWriteOnly) != 0) {
ASSERT((mode & kWrite) == 0);
flags = (O_WRONLY | O_CREAT);
}
if ((mode & kTruncate) != 0) {
flags = flags | O_TRUNC;
}
Expand All @@ -167,7 +172,8 @@ File* File::Open(const char* name, FileOpenMode mode) {
if (fd < 0) {
return NULL;
}
if (((mode & kWrite) != 0) && ((mode & kTruncate) == 0)) {
if ((((mode & kWrite) != 0) && ((mode & kTruncate) == 0)) ||
(((mode & kWriteOnly) != 0) && ((mode & kTruncate) == 0))) {
int64_t position = NO_RETRY_EXPECTED(lseek64(fd, 0, SEEK_END));
if (position < 0) {
return NULL;
Expand Down
8 changes: 7 additions & 1 deletion runtime/bin/file_macos.cc
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,13 @@ File* File::Open(const char* name, FileOpenMode mode) {
}
int flags = O_RDONLY;
if ((mode & kWrite) != 0) {
ASSERT((mode & kWriteOnly) == 0);
flags = (O_RDWR | O_CREAT);
}
if ((mode & kWriteOnly) != 0) {
ASSERT((mode & kWrite) == 0);
flags = (O_WRONLY | O_CREAT);
}
if ((mode & kTruncate) != 0) {
flags = flags | O_TRUNC;
}
Expand All @@ -169,7 +174,8 @@ File* File::Open(const char* name, FileOpenMode mode) {
return NULL;
}
FDUtils::SetCloseOnExec(fd);
if (((mode & kWrite) != 0) && ((mode & kTruncate) == 0)) {
if ((((mode & kWrite) != 0) && ((mode & kTruncate) == 0)) ||
(((mode & kWriteOnly) != 0) && ((mode & kTruncate) == 0))) {
int64_t position = lseek(fd, 0, SEEK_END);
if (position < 0) {
return NULL;
Expand Down
8 changes: 7 additions & 1 deletion runtime/bin/file_win.cc
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,13 @@ int64_t File::Length() {
File* File::Open(const char* name, FileOpenMode mode) {
int flags = O_RDONLY | O_BINARY | O_NOINHERIT;
if ((mode & kWrite) != 0) {
ASSERT((mode & kWriteOnly) == 0);
flags = (O_RDWR | O_CREAT | O_BINARY | O_NOINHERIT);
}
if ((mode & kWriteOnly) != 0) {
ASSERT((mode & kWrite) == 0);
flags = (O_WRONLY | O_CREAT | O_BINARY | O_NOINHERIT);
}
if ((mode & kTruncate) != 0) {
flags = flags | O_TRUNC;
}
Expand All @@ -168,7 +173,8 @@ File* File::Open(const char* name, FileOpenMode mode) {
if (fd < 0) {
return NULL;
}
if (((mode & kWrite) != 0) && ((mode & kTruncate) == 0)) {
if ((((mode & kWrite) != 0) && ((mode & kTruncate) == 0)) ||
(((mode & kWriteOnly) != 0) && ((mode & kTruncate) == 0))) {
int64_t position = _lseeki64(fd, 0, SEEK_END);
if (position < 0) {
return NULL;
Expand Down
18 changes: 16 additions & 2 deletions sdk/lib/io/file.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,20 @@ part of dart.io;
class FileMode {
/// The mode for opening a file only for reading.
static const READ = const FileMode._internal(0);
/// The mode for opening a file for reading and writing. The file is
/// Mode for opening a file for reading and writing. The file is
/// overwritten if it already exists. The file is created if it does not
/// already exist.
static const WRITE = const FileMode._internal(1);
/// The mode for opening a file for reading and writing to the
/// Mode for opening a file for reading and writing to the
/// end of it. The file is created if it does not already exist.
static const APPEND = const FileMode._internal(2);
/// Mode for opening a file for writing *only*. The file is
/// overwritten if it already exists. The file is created if it does not
/// already exist.
static const WRITE_ONLY = const FileMode._internal(3);
/// Mode for opening a file for writing *only* to the
/// end of it. The file is created if it does not already exist.
static const WRITE_ONLY_APPEND = const FileMode._internal(4);
final int _mode;

const FileMode._internal(this._mode);
Expand All @@ -31,6 +38,13 @@ const WRITE = FileMode.WRITE;
/// The mode for opening a file for reading and writing to the
/// end of it. The file is created if it does not already exist.
const APPEND = FileMode.APPEND;
/// Mode for opening a file for writing *only*. The file is
/// overwritten if it already exists. The file is created if it does not
/// already exist.
const WRITE_ONLY = FileMode.WRITE_ONLY;
/// Mode for opening a file for writing *only* to the
/// end of it. The file is created if it does not already exist.
const WRITE_ONLY_APPEND = FileMode.WRITE_ONLY_APPEND;


/// Type of lock when requesting a lock on a file.
Expand Down
22 changes: 13 additions & 9 deletions sdk/lib/io/file_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -344,8 +344,11 @@ class _File extends FileSystemEntity implements File {
Future<RandomAccessFile> open({FileMode mode: FileMode.READ}) {
if (mode != FileMode.READ &&
mode != FileMode.WRITE &&
mode != FileMode.APPEND) {
return new Future.error(new ArgumentError());
mode != FileMode.APPEND &&
mode != FileMode.WRITE_ONLY &&
mode != FileMode.WRITE_ONLY_APPEND) {
return new Future.error(
new ArgumentError('Invalid file mode for this operation'));
}
return _IOService._dispatch(_FILE_OPEN, [path, mode._mode])
.then((response) {
Expand Down Expand Up @@ -401,10 +404,10 @@ class _File extends FileSystemEntity implements File {
RandomAccessFile openSync({FileMode mode: FileMode.READ}) {
if (mode != FileMode.READ &&
mode != FileMode.WRITE &&
mode != FileMode.APPEND) {
throw new FileSystemException("Unknown file mode. Use FileMode.READ, "
"FileMode.WRITE or FileMode.APPEND.",
path);
mode != FileMode.APPEND &&
mode != FileMode.WRITE_ONLY &&
mode != FileMode.WRITE_ONLY_APPEND) {
throw new ArgumentError('Invalid file mode for this operation');
}
var id = _open(path, mode._mode);
throwIfError(id, "Cannot open file", path);
Expand All @@ -428,9 +431,10 @@ class _File extends FileSystemEntity implements File {
IOSink openWrite({FileMode mode: FileMode.WRITE,
Encoding encoding: UTF8}) {
if (mode != FileMode.WRITE &&
mode != FileMode.APPEND) {
throw new ArgumentError(
"Wrong FileMode. Use FileMode.WRITE or FileMode.APPEND");
mode != FileMode.APPEND &&
mode != FileMode.WRITE_ONLY &&
mode != FileMode.WRITE_ONLY_APPEND) {
throw new ArgumentError('Invalid file mode for this operation');
}
var consumer = new _FileStreamConsumer(this, mode);
return new IOSink(consumer, encoding: encoding);
Expand Down
84 changes: 84 additions & 0 deletions tests/standalone/io/file_write_only_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
//
// Dart test program for testing file I/O.

import 'dart:async';
import 'dart:io';

import "package:async_helper/async_helper.dart";
import "package:expect/expect.dart";

Future withTempDir(String prefix, void test(Directory dir)) async {
var tempDir = Directory.systemTemp.createTempSync(prefix);
try {
await test(tempDir);
} finally {
tempDir.deleteSync(recursive: true);
}
}

void withTempDirSync(String prefix, void test(Directory dir)) async {
var tempDir = Directory.systemTemp.createTempSync(prefix);
try {
test(tempDir);
} finally {
tempDir.deleteSync(recursive: true);
}
}

Futire expectThrowsAsync(Future future, String message) {
return future.then((r) => Expect.fail(message))
.catchError((e) {});
}


Future write(Directory dir) async {
var f = new File('x');
var raf = await f.open(mode: WRITE_ONLY);
await raf.writeString('Hello');
await raf.setPosition(0);
await raf.writeString('Hello');
await raf.setPosition(0);
await expectThrowsAsync(
raf.readByte(), 'Read from write only file succeeded');
await raf.close();
raf = await f.open(mode: WRITE_ONLY_APPEND);
await raf.writeString('Hello');
await expectThrowsAsync(
raf.readByte(), 'Read from write only file succeeded');
await raf.setPosition(0);
await raf.writeString('Hello');
await raf.close();
Expect.equals(f.lengthSync(), 10);
}

Future writeSync(Directory dir) {
var f = new File('x');
var raf = f.openSync(mode: WRITE_ONLY);
raf.writeStringSync('Hello');
raf.setPositionSync(0);
raf.writeStringSync('Hello');
raf.setPositionSync(0);
Expect.throws(() => raf.readByteSync());
}

Future openWrite(Directory dir) async {
var f = new File('x');
var sink = f.openWrite(mode: WRITE_ONLY);
sink.write('Hello');
await sink.close();
sink = await f.openWrite(mode: WRITE_ONLY_APPEND);
sink.write('Hello');
await sink.close();
Expect.equals(f.lengthSync(), 10);
}

main() async {
asyncStart();
await withTempDir('file_write_only_test', write);
withTempDirSync('file_write_only_test', writeSync);
await withTempDir('file_write_only_test', openWrite);
asyncEnd();
}

0 comments on commit eaeecf2

Please sign in to comment.