(path: string, label: string, maxBytes: number)
| 730 | } |
| 731 | |
| 732 | async function readExactFile(path: string, label: string, maxBytes: number): Promise<Buffer> { |
| 733 | let metadata; |
| 734 | try { |
| 735 | metadata = await lstat(path, { bigint: true }); |
| 736 | } catch (error) { |
| 737 | if (isNodeError(error, 'ENOENT')) throw error; |
| 738 | throw memoryBundleIoFailed(`${label} could not be inspected`, error); |
| 739 | } |
| 740 | if (!metadata.isFile() || metadata.isSymbolicLink()) { |
| 741 | throw invalidMemoryDocument(`${label} must be a regular file`); |
| 742 | } |
| 743 | if (metadata.size > BigInt(maxBytes)) { |
| 744 | throw invalidMemoryDocument(`${label} exceeds its ${maxBytes} byte scan limit`); |
| 745 | } |
| 746 | |
| 747 | const flags = |
| 748 | process.platform === 'win32' |
| 749 | ? constants.O_RDONLY |
| 750 | : constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK; |
| 751 | let handle: Awaited<ReturnType<typeof open>>; |
| 752 | try { |
| 753 | handle = await open(path, flags); |
| 754 | } catch (error) { |
| 755 | if (process.platform !== 'win32' && isNodeError(error, 'ELOOP')) { |
| 756 | throw invalidMemoryDocument(`${label} must not be a symbolic link`, error); |
| 757 | } |
| 758 | throw error; |
| 759 | } |
| 760 | try { |
| 761 | const opened = await handle.stat({ bigint: true }); |
| 762 | if (!opened.isFile() || opened.size > BigInt(maxBytes)) { |
| 763 | throw invalidMemoryDocument(`${label} is not a bounded regular file`); |
| 764 | } |
| 765 | const chunks: Buffer[] = []; |
| 766 | let total = 0; |
| 767 | for (;;) { |
| 768 | const remaining = maxBytes + 1 - total; |
| 769 | if (remaining <= 0) { |
| 770 | throw invalidMemoryDocument(`${label} exceeds its ${maxBytes} byte scan limit`); |
| 771 | } |
| 772 | const buffer = Buffer.allocUnsafe(Math.min(READ_CHUNK_BYTES, remaining)); |
| 773 | const { bytesRead } = await handle.read(buffer, 0, buffer.length, total); |
| 774 | if (bytesRead === 0) break; |
| 775 | total += bytesRead; |
| 776 | chunks.push(buffer.subarray(0, bytesRead)); |
| 777 | } |
| 778 | return Buffer.concat(chunks, total); |
| 779 | } finally { |
| 780 | await handle.close(); |
| 781 | } |
| 782 | } |
| 783 | |
| 784 | async function readBackupCandidate( |
| 785 | directory: DirectoryBinding, |
no test coverage detected