open static method
Implementation
static Future<OfficialFileStorage> open(String path,
{int maxImageBytes = 64 * 1024 * 1024}) async {
if (maxImageBytes < 100) throw RangeError.value(maxImageBytes);
RandomAccessFile? file;
RandomAccessFile? shm;
int? walLock;
var writable = true;
try {
try {
file = await File(path).open(mode: FileMode.append);
} on FileSystemException {
writable = false;
file = await File(path).open(mode: FileMode.read);
}
if (!await _tryLock(file, FileLock.shared, SqliteLock.sharedFirst,
SqliteLock.sharedFirst + 1)) {
throw const SqlException('database is locked');
}
final length = await file.length();
Uint8List? image;
final journalFile = File('$path-journal');
if (journalFile.existsSync() && writable) {
final journal = await journalFile.readAsBytes();
if (HotJournal.looksHot(journal) &&
await _tryLock(file, FileLock.exclusive, SqliteLock.reservedByte,
SqliteLock.reservedByte + 1)) {
try {
final superName = HotJournal.superJournalName(journal);
final blocked = superName != null && !File(superName).existsSync();
if (!blocked) {
if (length > maxImageBytes) {
throw const FormatException('Image limit exceeded');
}
image =
HotJournal.recover(await _readFully(file, length), journal);
}
} finally {
await file.unlock(
SqliteLock.reservedByte, SqliteLock.reservedByte + 1);
}
}
}
final walFile = File('$path-wal');
if (walFile.existsSync()) {
final shmFile = File('$path-shm');
// A live SQLite connection keeps the locks in `-shm`. Creating that
// file would change the sidecar set, so a leftover WAL with no
// `-shm` is recovered without one.
if (shmFile.existsSync()) {
shm = await shmFile.open(mode: FileMode.read);
for (var byte = SqliteLock.walRead;
byte < SqliteLock.walRead + SqliteLock.walReadCount;
byte++) {
if (await _tryLock(shm, FileLock.shared, byte, byte + 1)) {
walLock = byte;
break;
}
}
if (walLock == null) {
throw const SqlException('database is locked');
}
}
final wal = await walFile.readAsBytes();
final base = image ?? await _readFully(file, length);
if (base.length > maxImageBytes) {
throw const FormatException('Image limit exceeded');
}
image = WalSnapshot.recover(base, wal, maxImageBytes: maxImageBytes);
}
return OfficialFileStorage._(file, image, shm, walLock);
} catch (_) {
if (shm != null) {
try {
if (walLock != null) {
await shm.unlock(walLock, walLock + 1);
}
} catch (_) {}
await shm.close();
}
if (file != null) {
try {
await file.unlock(SqliteLock.sharedFirst, SqliteLock.sharedFirst + 1);
} catch (_) {}
await file.close();
}
rethrow;
}
}