diskFileSystemProvider.ts ×28

Frontier kind: Code frontier

unlabeled · c_05068fe448dd

12 tests · 14908 LOC · 65 files · introduces 0 tests · 135 LOC · 2 files

Introduces — evidence that enters the hierarchy at this concept

Code
30 ranges135 lines · 2 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
2406 ranges14908 lines · 65 files · Browse complete extent
All tests (intent)
12 testsBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

No tests are introduced at this concept. Its intent tests are introduced by other concepts.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

2 files ranked by introduced lines: 135 introduced LOC across 30 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/platform/files/node/diskFileSystemProvider.ts 120 introduced LOC · 28 ranges

Open complete file

100
101 private async statIgnoreError(resource: URI): Promise<IStat | undefined> {
103 > return await this.stat(resource);
104 > } catch (error) {
105 return undefined;
106 }
108
109 async realpath(resource: URI): Promise<string> {
170
171 private async createResourceLock(resource: URI): Promise<IDisposable> {
172 > const filePath = this.toFilePath(resource); diskFileSystemProvider.ts
173 > this.traceLock(`[Disk FileSystemProvider]: createResourceLock() - request to acquire resource lock (${filePath})`);
174 >
175 > // Await pending locks for resource. It is possible for a new lock being
176 > // added right after opening, so we have to loop over locks until no lock
177 > // remains.
178 > let existingLock: Barrier | undefined = undefined;
179 > while (existingLock = this.resourceLocks.get(resource)) {
180 this.traceLock(`[Disk FileSystemProvider]: createResourceLock() - waiting for resource lock to be released (${filePath})`);
181 await existingLock.wait();
182 }
184 > // Store new
185 > const newLock = new Barrier();
186 > this.resourceLocks.set(resource, newLock);
187 >
188 > this.traceLock(`[Disk FileSystemProvider]: createResourceLock() - new resource lock created (${filePath})`);
189 >
190 > return toDisposable(() => {
191 > this.traceLock(`[Disk FileSystemProvider]: createResourceLock() - resource lock dispose() (${filePath})`);
192 >
193 > // Delete lock if it is still ours
194 > if (this.resourceLocks.get(resource) === newLock) {
195 > this.traceLock(`[Disk FileSystemProvider]: createResourceLock() - resource lock removed from resource-lock map (${filePath})`);
196 > this.resourceLocks.delete(resource);
197 > }
198 >
199 > // Open lock
200 > this.traceLock(`[Disk FileSystemProvider]: createResourceLock() - resource lock barrier open() (${filePath})`);
201 > newLock.open();
202 > });
203 > }
204
205 async readFile(resource: URI, options?: IFileAtomicReadOptions): Promise<Uint8Array> {
226
227 private traceLock(msg: string): void {
228 > if (DiskFileSystemProvider.TRACE_LOG_RESOURCE_LOCKS) { diskFileSystemProvider.ts
229 this.logService.trace(msg);
230 }
232
233 readFileStream(resource: URI, opts: IFileReadStreamOptions, token: CancellationToken): ReadableStreamEvents<Uint8Array> {
243
244 async writeFile(resource: URI, content: Uint8Array, opts: IFileWriteOptions): Promise<void> {
245 > if (opts?.atomic !== false && opts?.atomic?.postfix && await this.canWriteFileAtomic(resource)) { diskFileSystemProvider.ts
246 return this.doWriteFileAtomic(resource, joinPath(resourcesDirname(resource), `${resourcesBasename(resource)}${opts.atomic.postfix}`), content, opts);
247 > } else { diskFileSystemProvider.ts
248 return this.doWriteFile(resource, content, opts);
249 }
251
252 private async canWriteFileAtomic(resource: URI): Promise<boolean> {
306
307 private async doWriteFile(resource: URI, content: Uint8Array, opts: IFileWriteOptions, disableWriteLock?: boolean): Promise<void> {
308 > let handle: number | undefined = undefined; diskFileSystemProvider.ts
309 > try {
310 > const filePath = this.toFilePath(resource);
311 >
312 > // Validate target unless { create: true, overwrite: true }
313 > if (!opts.create || !opts.overwrite) {
314 const fileExists = await Promises.exists(filePath);
315 if (fileExists) {
323 }
324 }
326 > // Open
327 > handle = await this.open(resource, { create: true, append: opts.append, unlock: opts.unlock }, disableWriteLock);
328 >
329 > // Write content at once
330 > await this.write(handle, 0, content, 0, content.byteLength);
331 > } catch (error) {
332 throw await this.toFileSystemProviderWriteError(resource, error);
333 > } finally { diskFileSystemProvider.ts
334 > if (typeof handle === 'number') {
335 > await this.close(handle);
336 > }
337 > }
338 > }
339
340 private readonly mapHandleToPos = new Map<number, number>();
412 if (typeof fd !== 'number') {
413 fd = await Promises.open(filePath, isFileOpenForWriteOptions(opts) ?
414 > // We take `opts.create` as a hint that the file is opened for writing diskFileSystemProvider.ts
415 > // as such we use 'w' to truncate an existing or create the
416 > // file otherwise. we do not allow reading.
417 > // If `opts.append` is true, use 'a' to append to the file.
418 > (opts.append ? 'a' : 'w') :
419 // Otherwise we assume the file is opened for reading
420 // as such we use 'r' to neither truncate, nor create
447 // remember that this handle was used for writing
448 if (isFileOpenForWriteOptions(opts)) {
449 > this.writeHandles.set(fd, resource); diskFileSystemProvider.ts
450 > }
451
452 if (lock) {
490 // to flush the contents to disk if possible.
491 if (this.writeHandles.delete(fd) && DiskFileSystemProvider.canFlush) {
493 > await Promises.fdatasync(fd); // https://github.com/microsoft/vscode/issues/9589
494 > } catch (error) {
495 // In some exotic setups it is well possible that node fails to sync
496 // In that case we disable flushing and log the error to our logger
590
591 async write(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number> {
593 > // We know at this point that the file to write to is truncated and thus empty
594 > // if the write now fails, the file remains empty. as such we really try hard
595 > // to ensure the write succeeds by retrying up to three times.
596 > return retry(() => this.doWrite(fd, pos, data, offset, length), 100 /* ms delay */, 3 /* retries */);
597 > }
598
599 private async doWrite(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number> {
600 > const normalizedPos = this.normalizePos(fd, pos); diskFileSystemProvider.ts
601 >
602 > let bytesWritten: number | null = null;
603 > try {
604 > bytesWritten = (await Promises.write(fd, data, offset, length, normalizedPos)).bytesWritten;
605 > } catch (error) {
606 throw await this.toFileSystemProviderWriteError(this.writeHandles.get(fd), error);
607 > } finally { diskFileSystemProvider.ts
608 > this.updatePos(fd, normalizedPos, bytesWritten);
609 > }
610 >
611 > return bytesWritten;
612 > }
613
614 //#endregion
669
670 async rename(from: URI, to: URI, opts: IFileOverwriteOptions): Promise<void> {
671 > const fromFilePath = this.toFilePath(from); diskFileSystemProvider.ts
672 > const toFilePath = this.toFilePath(to);
673 >
674 > if (fromFilePath === toFilePath) {
675 return; // simulate node.js behaviour here and do a no-op if paths match
676 }
678 > try {
679 >
680 > // Validate the move operation can perform
681 > await this.validateMoveCopy(from, to, 'move', opts.overwrite);
682 >
683 > // Rename
684 > await Promises.rename(fromFilePath, toFilePath);
685 > } catch (error) {
686
687 // Rewrite some typical errors that can happen especially around symlinks
693 throw this.toFileSystemProviderError(error);
694 }
696
697 async copy(from: URI, to: URI, opts: IFileOverwriteOptions): Promise<void> {
723
724 private async validateMoveCopy(from: URI, to: URI, mode: 'move' | 'copy', overwrite?: boolean): Promise<void> {
725 > const fromFilePath = this.toFilePath(from); diskFileSystemProvider.ts
726 > const toFilePath = this.toFilePath(to);
727 >
728 > let isSameResourceWithDifferentPathCase = false;
729 > const isPathCaseSensitive = !!(this.capabilities & FileSystemProviderCapabilities.PathCaseSensitive);
730 > if (!isPathCaseSensitive) {
731 isSameResourceWithDifferentPathCase = isEqual(fromFilePath, toFilePath, true /* ignore case */);
732 }
734 > if (isSameResourceWithDifferentPathCase) {
735
736 // You cannot copy the same file to the same location with different
746 }
747 }
749 > // Here we have to see if the target to move/copy to exists or not.
750 > // We need to respect the `overwrite` option to throw in case the
751 > // target exists.
752 >
753 > const fromStat = await this.statIgnoreError(from);
754 > if (!fromStat) {
755 throw createFileSystemProviderError(localize('fileMoveCopyErrorNotFound', "File to move/copy does not exist"), FileSystemProviderErrorCode.FileNotFound);
756 }
758 > const toStat = await this.statIgnoreError(to);
759 > if (!toStat) {
760 return; // target does not exist so we are good
761 }
766
767 // Handle existing target for move/copy
768 > if ((fromStat.type & FileType.File) !== 0 && (toStat.type & FileType.File) !== 0) { diskFileSystemProvider.ts
769 return; // node.js can move/copy a file over an existing file without having to delete it first
770 } else {
771 await this.delete(to, { recursive: true, useTrash: false, atomic: false });
772 }
774
775 //#endregion
src/vs/base/node/pfs.ts 15 introduced LOC · 2 ranges

Open complete file

810
811 get write() {
812 > pfs.ts
813 > // Not using `promisify` here for a reason: the return
814 > // type is not an object as indicated by TypeScript but
815 > // just the bytes written, so we create our own wrapper.
816 >
817 > return (fd: number, buffer: Uint8Array, offset: number | undefined | null, length: number | undefined | null, position: number | undefined | null) => {
818 > return new Promise<{ bytesWritten: number; buffer: Uint8Array }>((resolve, reject) => {
819 > fs.write(fd, buffer, offset, length, position, (err, bytesWritten, buffer) => {
820 > if (err) {
821 return reject(err);
822 }
823 > pfs.ts
824 > return resolve({ bytesWritten, buffer });
825 > });
826 > });
827 > };
828 > }
829
830 get fdatasync() { return promisify(fs.fdatasync); } // not exposed as API in 22.x yet