// 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. part of "dart:io"; // Read the file in blocks of size 64k. const int _blockSize = 64 * 1024; // The maximum number of bytes to read in a single call to `read`. // // On Windows and macOS, it is an error to call // `read/_read(fildes, buf, nbyte)` with `nbyte >= INT_MAX`. // // The POSIX specification states that the behavior of `read` is // implementation-defined if `nbyte > SSIZE_MAX`. On Linux, the `read` will // transfer at most 0x7ffff000 bytes and return the number of bytes actually. // transfered. // // A smaller value has the advantage of: // 1. making vm-service clients (e.g. debugger) more responsive // 2. reducing memory overhead (since `readInto` copies memory) // // A bigger value reduces the number of system calls. const int _maxReadSize = 16 * 1024 * 1024; // 16MB. class _FileStream extends Stream> { // Stream controller. late StreamController _controller; // Information about the underlying file. String? _path; RandomAccessFile? _openedFile; int _position; int? _end; final Completer _closeCompleter = new Completer(); // Has the stream been paused or unsubscribed? bool _unsubscribed = false; // Is there a read currently in progress? bool _readInProgress = true; bool _closed = false; bool _atEnd = false; _FileStream(this._path, int? position, this._end) : _position = position ?? 0; _FileStream.forStdin() : _position = 0; _FileStream.forRandomAccessFile(RandomAccessFile f) : _position = 0, _openedFile = f; StreamSubscription listen( void onData(Uint8List event)?, { Function? onError, void onDone()?, bool? cancelOnError, }) { _controller = new StreamController( sync: true, onListen: _start, onResume: _readBlock, onCancel: () { _unsubscribed = true; return _closeFile(); }, ); return _controller.stream.listen( onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError, ); } Future _closeFile() { if (_readInProgress || _closed) { return _closeCompleter.future; } _closed = true; void done() { _closeCompleter.complete(); _controller.close(); } _openedFile!.close().catchError(_controller.addError).whenComplete(done); return _closeCompleter.future; } void _readBlock() { // Don't start a new read if one is already in progress. if (_readInProgress) return; if (_atEnd) { _closeFile(); return; } _readInProgress = true; int readBytes = _blockSize; final end = _end; if (end != null) { readBytes = min(readBytes, end - _position); if (readBytes < 0) { _readInProgress = false; if (!_unsubscribed) { _controller.addError(new RangeError("Bad end position: $end")); _closeFile(); _unsubscribed = true; } return; } } _openedFile! .read(readBytes) .then((block) { _readInProgress = false; if (_unsubscribed) { _closeFile(); return; } _position += block.length; // read() may return less than `readBytes` if `_openFile` is a pipe or // terminal or if a signal is received. Only a empty return indicates // that the write side of the pipe is closed or that we are at the end // of a file. // See https://man7.org/linux/man-pages/man2/read.2.html if (block.length == 0 || (_end != null && _position == _end)) { _atEnd = true; } if (!_atEnd && !_controller.isPaused) { _readBlock(); } if (block.length > 0) { _controller.add(block); } if (_atEnd) { _closeFile(); } }) .catchError((e, s) { if (!_unsubscribed) { _controller.addError(e, s); _closeFile(); _unsubscribed = true; } }); } void _start() { if (_position < 0) { _controller.addError(new RangeError("Bad start position: $_position")); _controller.close(); _closeCompleter.complete(); return; } void onReady(RandomAccessFile file) { _openedFile = file; _readInProgress = false; _readBlock(); } void onOpenFile(RandomAccessFile file) { if (_position > 0) { file .setPosition(_position) .then( onReady, onError: (e, s) { _controller.addError(e, s); _readInProgress = false; _closeFile(); }, ); } else { onReady(file); } } void openFailed(error, stackTrace) { _controller.addError(error, stackTrace); _controller.close(); _closeCompleter.complete(); } final path = _path; final openedFile = _openedFile; if (openedFile != null) { onOpenFile(openedFile); } else if (path != null) { new File( path, ).open(mode: FileMode.read).then(onOpenFile, onError: openFailed); } else { try { onOpenFile(_File._openStdioSync(0)); } catch (e, s) { openFailed(e, s); } } } } class _FileStreamConsumer implements StreamConsumer> { File? _file; Future _openFuture; _FileStreamConsumer(File file, FileMode mode) : _file = file, _openFuture = file.open(mode: mode); _FileStreamConsumer._fromStdio(int fd) : _openFuture = new Future.value(_File._openStdioSync(fd)); _FileStreamConsumer.fromRandomAccessFile(RandomAccessFile f) : _openFuture = Future.value(f); Future addStream(Stream> stream) { Completer completer = new Completer.sync(); _openFuture .then((openedFile) { late StreamSubscription> _subscription; void error(e, StackTrace stackTrace) { _subscription.cancel(); openedFile.close(); completer.completeError(e, stackTrace); } _subscription = stream.listen( (d) { _subscription.pause(); try { openedFile .writeFrom(d, 0, d.length) .then((_) => _subscription.resume(), onError: error); } catch (e, stackTrace) { error(e, stackTrace); } }, onDone: () { completer.complete(_file); }, onError: error, cancelOnError: true, ); }) .catchError(completer.completeError); return completer.future; } Future close() => _openFuture.then((openedFile) => openedFile.close()).then((_) => _file); } // Class for encapsulating the native implementation of files. class _File extends FileSystemEntity implements File { final String _path; final Uint8List _rawPath; _File(this._path) : _rawPath = FileSystemEntity._toUtf8Array(_path); _File.fromRawPath(Uint8List rawPath) : _rawPath = FileSystemEntity._toNullTerminatedUtf8Array(rawPath), _path = FileSystemEntity._toStringFromUtf8Array(rawPath); String get path => _path; // WARNING: // Calling this function will increase the reference count on the native // namespace object. It should only be called to pass the pointer to the // IOService, which will decrement the reference count when it is finished // with it. static int _namespacePointer() => _Namespace._namespacePointer; static Future _dispatchWithNamespace(int request, List data) { data[0] = _namespacePointer(); return _IOService._dispatch(request, data); } Future exists() { return _dispatchWithNamespace(_IOService.fileExists, [null, _rawPath]).then( (response) { _checkForErrorResponse(response, "Cannot check existence", path); return response as bool; }, ); } external static _exists(_Namespace namespace, Uint8List rawPath); bool existsSync() { var result = _exists(_Namespace._namespace, _rawPath); throwIfError(result, "Cannot check existence of file", path); return result; } File get absolute => new File(_absolutePath); Future create({bool recursive = false, bool exclusive = false}) { var result = recursive ? parent.create(recursive: true) : new Future.value(null); return result .then( (_) => _dispatchWithNamespace(_IOService.fileCreate, [ null, _rawPath, exclusive, ]), ) .then((response) { _checkForErrorResponse(response, "Cannot create file", path); return this; }); } external static _create( _Namespace namespace, Uint8List rawPath, bool exclusive, ); external static _createLink( _Namespace namespace, Uint8List rawPath, String target, ); external static List _createPipe(_Namespace namespace); external static _linkTarget(_Namespace namespace, Uint8List rawPath); void createSync({bool recursive = false, bool exclusive = false}) { if (recursive) { parent.createSync(recursive: true); } var result = _create(_Namespace._namespace, _rawPath, exclusive); throwIfError(result, "Cannot create file", path); } Future _delete({bool recursive = false}) { if (recursive) { return new Directory(path).delete(recursive: true).then((_) => this); } return _dispatchWithNamespace(_IOService.fileDelete, [null, _rawPath]).then( (response) { _checkForErrorResponse(response, "Cannot delete file", path); return this; }, ); } external static _deleteNative(_Namespace namespace, Uint8List rawPath); external static _deleteLinkNative(_Namespace namespace, Uint8List rawPath); void _deleteSync({bool recursive = false}) { if (recursive) { return new Directory.fromRawPath(_rawPath).deleteSync(recursive: true); } var result = _deleteNative(_Namespace._namespace, _rawPath); throwIfError(result, "Cannot delete file", path); } Future rename(String newPath) { return _dispatchWithNamespace(_IOService.fileRename, [ null, _rawPath, newPath, ]).then((response) { _checkForErrorResponse( response, "Cannot rename file to '$newPath'", path, ); return new File(newPath); }); } external static _rename( _Namespace namespace, Uint8List oldPath, String newPath, ); external static _renameLink( _Namespace namespace, Uint8List oldPath, String newPath, ); File renameSync(String newPath) { var result = _rename(_Namespace._namespace, _rawPath, newPath); throwIfError(result, "Cannot rename file to '$newPath'", path); return new File(newPath); } Future copy(String newPath) { return _dispatchWithNamespace(_IOService.fileCopy, [ null, _rawPath, newPath, ]).then((response) { _checkForErrorResponse(response, "Cannot copy file to '$newPath'", path); return new File(newPath); }); } external static _copy( _Namespace namespace, Uint8List oldPath, String newPath, ); File copySync(String newPath) { var result = _copy(_Namespace._namespace, _rawPath, newPath); throwIfError(result, "Cannot copy file to '$newPath'", path); return new File(newPath); } Future open({FileMode mode = FileMode.read}) { if (mode != FileMode.read && mode != FileMode.write && mode != FileMode.append && mode != FileMode.writeOnly && mode != FileMode.writeOnlyAppend) { return new Future.error( new ArgumentError('Invalid file mode for this operation'), ); } return _dispatchWithNamespace(_IOService.fileOpen, [ null, _rawPath, mode._mode, ]).then((response) { _checkForErrorResponse(response, "Cannot open file", path); return _RandomAccessFile(response as int, path); }); } Future length() { return _dispatchWithNamespace(_IOService.fileLengthFromPath, [ null, _rawPath, ]).then((response) { _checkForErrorResponse(response, "Cannot retrieve length of file", path); return response as int; }); } external static _lengthFromPath(_Namespace namespace, Uint8List rawPath); int lengthSync() { var result = _lengthFromPath(_Namespace._namespace, _rawPath); throwIfError(result, "Cannot retrieve length of file", path); return result; } Future lastAccessed() { return _dispatchWithNamespace(_IOService.fileLastAccessed, [ null, _rawPath, ]).then((response) { _checkForErrorResponse(response, "Cannot retrieve access time", path); return DateTime.fromMillisecondsSinceEpoch(response as int); }); } external static _lastAccessed(_Namespace namespace, Uint8List rawPath); DateTime lastAccessedSync() { var ms = _lastAccessed(_Namespace._namespace, _rawPath); throwIfError(ms, "Cannot retrieve access time", path); return new DateTime.fromMillisecondsSinceEpoch(ms); } Future setLastAccessed(DateTime time) { int millis = time.millisecondsSinceEpoch; return _dispatchWithNamespace(_IOService.fileSetLastAccessed, [ null, _rawPath, millis, ]).then((response) { _checkForErrorResponse(response, "Cannot set access time", path); return null; }); } external static _setLastAccessed( _Namespace namespace, Uint8List rawPath, int millis, ); void setLastAccessedSync(DateTime time) { int millis = time.millisecondsSinceEpoch; var result = _setLastAccessed(_Namespace._namespace, _rawPath, millis); if (result is OSError) { throw new FileSystemException( "Failed to set file access time", path, result, ); } } Future lastModified() { return _dispatchWithNamespace(_IOService.fileLastModified, [ null, _rawPath, ]).then((response) { _checkForErrorResponse( response, "Cannot retrieve modification time", path, ); return DateTime.fromMillisecondsSinceEpoch(response as int); }); } external static _lastModified(_Namespace namespace, Uint8List rawPath); DateTime lastModifiedSync() { var ms = _lastModified(_Namespace._namespace, _rawPath); throwIfError(ms, "Cannot retrieve modification time", path); return new DateTime.fromMillisecondsSinceEpoch(ms); } Future setLastModified(DateTime time) { int millis = time.millisecondsSinceEpoch; return _dispatchWithNamespace(_IOService.fileSetLastModified, [ null, _rawPath, millis, ]).then((response) { _checkForErrorResponse(response, "Cannot set modification time", path); return null; }); } external static _setLastModified( _Namespace namespace, Uint8List rawPath, int millis, ); void setLastModifiedSync(DateTime time) { int millis = time.millisecondsSinceEpoch; var result = _setLastModified(_Namespace._namespace, _rawPath, millis); if (result is OSError) { throw new FileSystemException( "Failed to set file modification time", path, result, ); } } external static _open(_Namespace namespace, Uint8List rawPath, int mode); RandomAccessFile openSync({FileMode mode = FileMode.read}) { if (mode != FileMode.read && mode != FileMode.write && mode != FileMode.append && mode != FileMode.writeOnly && mode != FileMode.writeOnlyAppend) { throw new ArgumentError('Invalid file mode for this operation'); } var id = _open(_Namespace._namespace, _rawPath, mode._mode); throwIfError(id, "Cannot open file", path); return new _RandomAccessFile(id, _path); } external static int _openStdio(int fd); static RandomAccessFile _openStdioSync(int fd) { var id = _openStdio(fd); if (id == 0) { throw new FileSystemException("Cannot open stdio file for: $fd"); } return new _RandomAccessFile(id, ""); } Stream> openRead([int? start, int? end]) { return new _FileStream(path, start, end); } IOSink openWrite({FileMode mode = FileMode.write, Encoding encoding = utf8}) { if (mode != FileMode.write && mode != FileMode.append && mode != FileMode.writeOnly && mode != FileMode.writeOnlyAppend) { throw new ArgumentError('Invalid file mode for this operation'); } var consumer = new _FileStreamConsumer(this, mode); return new IOSink(consumer, encoding: encoding); } Future readAsBytes() { Future readUnsized(RandomAccessFile file) { var builder = new BytesBuilder(copy: false); var completer = new Completer(); void read() { file.read(_blockSize).then((data) { if (data.length > 0) { builder.add(data); read(); } else { completer.complete(builder.takeBytes()); } }, onError: completer.completeError); } read(); return completer.future; } Future readSized(RandomAccessFile file, int length) { var data = Uint8List(length); var offset = 0; var completer = new Completer(); void read() { file.readInto(data, offset, min(offset + _maxReadSize, length)).then(( readSize, ) { if (readSize > 0) { offset += readSize; read(); } else { assert(readSize == 0); if (offset < length) { data = Uint8List.sublistView(data, 0, offset); } completer.complete(data); } }, onError: completer.completeError); } read(); return completer.future; } return open().then((file) { return file .length() .then((length) { if (length == 0) { // May be character device, try to read it in chunks. return readUnsized(file); } return readSized(file, length); }) .whenComplete(file.close); }); } Uint8List readAsBytesSync() { var opened = openSync(); try { Uint8List data; var length = opened.lengthSync(); if (length == 0) { // May be character device, try to read it in chunks. var builder = new BytesBuilder(copy: false); do { data = opened.readSync(_blockSize); if (data.length > 0) { builder.add(data); } } while (data.length > 0); data = builder.takeBytes(); } else { data = Uint8List(length); var offset = 0; while (offset < length) { final readSize = opened.readIntoSync( data, offset, min(offset + _maxReadSize, length), ); if (readSize == 0) { break; } offset += readSize; } if (offset < length) { data = Uint8List.view(data.buffer, 0, offset); } } return data; } finally { opened.closeSync(); } } String _tryDecode(List bytes, Encoding encoding) { try { return encoding.decode(bytes); } catch (_) { throw new FileSystemException( "Failed to decode data using encoding '${encoding.name}'", path, ); } } Future readAsString({Encoding encoding = utf8}) async => _tryDecode(await readAsBytes(), encoding); String readAsStringSync({Encoding encoding = utf8}) => _tryDecode(readAsBytesSync(), encoding); Future> readAsLines({Encoding encoding = utf8}) => readAsString(encoding: encoding).then(const LineSplitter().convert); List readAsLinesSync({Encoding encoding = utf8}) => const LineSplitter().convert(readAsStringSync(encoding: encoding)); Future writeAsBytes( List bytes, { FileMode mode = FileMode.write, bool flush = false, }) { return open(mode: mode).then((file) { return file .writeFrom(bytes, 0, bytes.length) .then((_) { if (flush) return file.flush().then((_) => this); return this; }) .whenComplete(file.close); }); } void writeAsBytesSync( List bytes, { FileMode mode = FileMode.write, bool flush = false, }) { RandomAccessFile opened = openSync(mode: mode); try { opened.writeFromSync(bytes, 0, bytes.length); if (flush) opened.flushSync(); } finally { opened.closeSync(); } } Future writeAsString( String contents, { FileMode mode = FileMode.write, Encoding encoding = utf8, bool flush = false, }) { try { return writeAsBytes(encoding.encode(contents), mode: mode, flush: flush); } catch (e) { return new Future.error(e); } } void writeAsStringSync( String contents, { FileMode mode = FileMode.write, Encoding encoding = utf8, bool flush = false, }) { writeAsBytesSync(encoding.encode(contents), mode: mode, flush: flush); } String toString() => "File: '$path'"; static throwIfError(Object result, String msg, String path) { if (result is OSError) { throw FileSystemException._fromOSError(result, msg, path); } } } abstract class _RandomAccessFileOps { external factory _RandomAccessFileOps._(int pointer); int _getPointer(); int get fd; int close(); readByte(); read(int bytes); readInto(List buffer, int start, int? end); writeByte(int value); writeFrom(List buffer, int start, int? end); position(); setPosition(int position); truncate(int length); length(); flush(); lock(int lock, int start, int end); } @pragma("vm:entry-point") class _RandomAccessFile implements RandomAccessFile { static bool _connectedResourceHandler = false; final String path; bool _asyncDispatched = false; late _FileResourceInfo _resourceInfo; _RandomAccessFileOps _ops; @pragma("vm:entry-point") _RandomAccessFile(int pointer, this.path) : _ops = new _RandomAccessFileOps._(pointer) { _resourceInfo = new _FileResourceInfo(this); _maybeConnectHandler(); } void _maybePerformCleanup() { if (closed) { _FileResourceInfo.fileClosed(_resourceInfo); } } _maybeConnectHandler() { if (!const bool.fromEnvironment("dart.vm.product") && !_connectedResourceHandler) { // TODO(ricow): We probably need to set these in some initialization code. // We need to make sure that these are always available from the // observatory even if no files (or sockets for the socket ones) are // open. registerExtension( 'ext.dart.io.getOpenFiles', _FileResourceInfo.getOpenFiles, ); registerExtension( 'ext.dart.io.getOpenFileById', _FileResourceInfo.getOpenFileInfoMapByID, ); _connectedResourceHandler = true; } } Future close() { return _dispatch(_IOService.fileClose, [null], markClosed: true).then(( result, ) { if (result == -1) { throw new FileSystemException("Cannot close file", path); } closed = closed || (result == 0); _maybePerformCleanup(); }); } void closeSync() { _checkAvailable(); var id = _ops.close(); if (id == -1) { throw new FileSystemException("Cannot close file", path); } closed = closed || (id == 0); _maybePerformCleanup(); } Future readByte() { return _dispatch(_IOService.fileReadByte, [null]).then((response) { _checkForErrorResponse(response, "readByte failed", path); _resourceInfo.addRead(1); return response as int; }); } int readByteSync() { _checkAvailable(); var result = _ops.readByte(); if (result is OSError) { throw new FileSystemException("readByte failed", path, result); } _resourceInfo.addRead(1); return result; } Future read(int bytes) { return _dispatch(_IOService.fileRead, [null, bytes]).then((response) { _checkForErrorResponse(response, "read failed", path); var result = (response as List)[1] as Uint8List; _resourceInfo.addRead(result.length); return result; }); } Uint8List readSync(int bytes) { _checkAvailable(); var result = _ops.read(bytes); if (result is! Uint8List) { throw new FileSystemException("readSync failed", path, result as OSError); } _resourceInfo.addRead(result.length); return result; } Future readInto(List buffer, [int start = 0, int? end]) { end = RangeError.checkValidRange(start, end, buffer.length); if (end == start) { return new Future.value(0); } int length = end - start; return _dispatch(_IOService.fileReadInto, [null, length]).then((response) { _checkForErrorResponse(response, "readInto failed", path); var responseList = response as List; var read = responseList[1] as int; var data = responseList[2] as List; buffer.setRange(start, start + read, data); _resourceInfo.addRead(read); return read; }); } int readIntoSync(List buffer, [int start = 0, int? end]) { _checkAvailable(); end = RangeError.checkValidRange(start, end, buffer.length); if (end == start) { return 0; } var result = _ops.readInto(buffer, start, end); if (result is OSError) { throw new FileSystemException("readInto failed", path, result); } _resourceInfo.addRead(result); return result; } Future writeByte(int value) { return _dispatch(_IOService.fileWriteByte, [null, value]).then((response) { _checkForErrorResponse(response, "writeByte failed", path); _resourceInfo.addWrite(1); return this; }); } int writeByteSync(int value) { _checkAvailable(); var result = _ops.writeByte(value); if (result is OSError) { throw new FileSystemException("writeByte failed", path, result); } _resourceInfo.addWrite(1); return result; } Future writeFrom( List buffer, [ int start = 0, int? end, ]) { end = RangeError.checkValidRange(start, end, buffer.length); if (end == start) { return new Future.value(this); } _BufferAndStart result; try { result = _ensureFastAndSerializableByteData(buffer, start, end); } catch (e) { return new Future.error(e); } List request = new List.filled(4, null); request[0] = null; request[1] = result.buffer; request[2] = result.start; request[3] = end - (start - result.start); return _dispatch(_IOService.fileWriteFrom, request).then((response) { _checkForErrorResponse(response, "writeFrom failed", path); _resourceInfo.addWrite(end! - (start - result.start)); return this; }); } void writeFromSync(List buffer, [int start = 0, int? end]) { _checkAvailable(); end = RangeError.checkValidRange(start, end, buffer.length); if (end == start) { return; } _BufferAndStart bufferAndStart = _ensureFastAndSerializableByteData( buffer, start, end, ); var result = _ops.writeFrom( bufferAndStart.buffer, bufferAndStart.start, end - (start - bufferAndStart.start), ); if (result is OSError) { throw new FileSystemException("writeFrom failed", path, result); } _resourceInfo.addWrite(end - (start - bufferAndStart.start)); } Future writeString( String string, { Encoding encoding = utf8, }) { var data = encoding.encode(string); return writeFrom(data, 0, data.length); } void writeStringSync(String string, {Encoding encoding = utf8}) { var data = encoding.encode(string); writeFromSync(data, 0, data.length); } Future position() { return _dispatch(_IOService.filePosition, [null]).then((response) { _checkForErrorResponse(response, "position failed", path); return response as int; }); } int positionSync() { _checkAvailable(); var result = _ops.position(); if (result is OSError) { throw new FileSystemException("position failed", path, result); } return result; } Future setPosition(int position) { return _dispatch(_IOService.fileSetPosition, [null, position]).then(( response, ) { _checkForErrorResponse(response, "setPosition failed", path); return this; }); } void setPositionSync(int position) { _checkAvailable(); var result = _ops.setPosition(position); if (result is OSError) { throw new FileSystemException("setPosition failed", path, result); } } Future truncate(int length) { return _dispatch(_IOService.fileTruncate, [null, length]).then((response) { _checkForErrorResponse(response, "truncate failed", path); return this; }); } void truncateSync(int length) { _checkAvailable(); var result = _ops.truncate(length); if (result is OSError) { throw new FileSystemException("truncate failed", path, result); } } Future length() { return _dispatch(_IOService.fileLength, [null]).then((response) { _checkForErrorResponse(response, "length failed", path); return response as int; }); } int lengthSync() { _checkAvailable(); var result = _ops.length(); if (result is OSError) { throw new FileSystemException("length failed", path, result); } return result; } Future flush() { return _dispatch(_IOService.fileFlush, [null]).then((response) { _checkForErrorResponse(response, "flush failed", path); return this; }); } void flushSync() { _checkAvailable(); var result = _ops.flush(); if (result is OSError) { throw new FileSystemException("flush failed", path, result); } } static const int lockUnlock = 0; // static const int lockShared = 1; // static const int lockExclusive = 2; // static const int lockBlockingShared = 3; // static const int lockBlockingExclusive = 4; int _fileLockValue(FileLock fl) => fl._type; Future lock([ FileLock mode = FileLock.exclusive, int start = 0, int end = -1, ]) { if ((start < 0) || (end < -1) || ((end != -1) && (start >= end))) { throw new ArgumentError(); } int lock = _fileLockValue(mode); return _dispatch(_IOService.fileLock, [null, lock, start, end]).then(( response, ) { _checkForErrorResponse(response, 'lock failed', path); return this; }); } Future unlock([int start = 0, int end = -1]) { if (start == end) { throw new ArgumentError(); } return _dispatch(_IOService.fileLock, [null, lockUnlock, start, end]).then(( response, ) { _checkForErrorResponse(response, 'unlock failed', path); return this; }); } void lockSync([ FileLock mode = FileLock.exclusive, int start = 0, int end = -1, ]) { _checkAvailable(); if ((start < 0) || (end < -1) || ((end != -1) && (start >= end))) { throw new ArgumentError(); } int lock = _fileLockValue(mode); var result = _ops.lock(lock, start, end); if (result is OSError) { throw new FileSystemException('lock failed', path, result); } } void unlockSync([int start = 0, int end = -1]) { _checkAvailable(); if (start == end) { throw new ArgumentError(); } var result = _ops.lock(lockUnlock, start, end); if (result is OSError) { throw new FileSystemException('unlock failed', path, result); } } bool closed = false; int get fd => _ops.fd; // WARNING: // Calling this function will increase the reference count on the native // object that implements the file operations. It should only be called to // pass the pointer to the IO Service, which will decrement the reference // count when it is finished with it. int _pointer() => _ops._getPointer(); Future _dispatch(int request, List data, {bool markClosed = false}) { if (closed) { return new Future.error(new FileSystemException("File closed", path)); } if (_asyncDispatched) { var msg = "An async operation is currently pending"; return new Future.error(new FileSystemException(msg, path)); } if (markClosed) { // Set closed to true to ensure that no more async requests can be issued // for this file. closed = true; } _asyncDispatched = true; data[0] = _pointer(); return _IOService._dispatch(request, data).whenComplete(() { _asyncDispatched = false; }); } void _checkAvailable() { if (_asyncDispatched) { throw new FileSystemException( "An async operation is currently pending", path, ); } if (closed) { throw new FileSystemException("File closed", path); } } } class _ReadPipe extends _FileStream implements ReadPipe { _ReadPipe(RandomAccessFile file) : super.forRandomAccessFile(file); } class _WritePipe extends _IOSinkImpl implements WritePipe { RandomAccessFile _file; _WritePipe(file) : this._file = file, super(_FileStreamConsumer.fromRandomAccessFile(file), utf8); } class _Pipe implements Pipe { final ReadPipe _readPipe; final WritePipe _writePipe; ReadPipe get read => _readPipe; WritePipe get write => _writePipe; _Pipe(this._readPipe, this._writePipe); static Future<_Pipe> create() { final completer = Completer<_Pipe>.sync(); _File._dispatchWithNamespace(_IOService.fileCreatePipe, [null]).then(( response, ) { final filePointers = (response as List).cast(); completer.complete( _Pipe( _ReadPipe(_RandomAccessFile(filePointers[0], '')), _WritePipe(_RandomAccessFile(filePointers[1], '')), ), ); }); return completer.future; } factory _Pipe.createSync() { final filePointers = _File._createPipe(_Namespace._namespace); return _Pipe( _ReadPipe(_RandomAccessFile(filePointers[0] as int, '')), _WritePipe(_RandomAccessFile(filePointers[1] as int, '')), ); } }