Skip to content

Commit 6dd6676

Browse files
authored
Merge pull request #67 from PermanentOrg/64-fix-stats
Generate correct file attributes
2 parents 60746f7 + c0165bf commit 6dd6676

File tree

4 files changed

+75
-51
lines changed

4 files changed

+75
-51
lines changed

src/classes/PermanentFileSystem.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import {
77
getRecord,
88
} from '@permanentorg/sdk';
99
import {
10+
generateAttributesForFile,
11+
generateAttributesForFolder,
1012
generateDefaultAttributes,
1113
generateFileEntry,
1214
generateFileEntriesForRecords,
@@ -21,7 +23,10 @@ import type {
2123
File,
2224
Record,
2325
} from '@permanentorg/sdk';
24-
import type { FileEntry } from 'ssh2';
26+
import type {
27+
Attributes,
28+
FileEntry,
29+
} from 'ssh2';
2530

2631
const isRootPath = (requestedPath: string): boolean => (
2732
requestedPath === '/'
@@ -99,6 +104,27 @@ export class PermanentFileSystem {
99104
throw new Error('Item was not found');
100105
}
101106

107+
public async getItemAttributes(itemPath: string): Promise<Attributes> {
108+
if (isRootPath(itemPath)
109+
|| isArchiveCataloguePath(itemPath)
110+
|| isArchivePath(itemPath)) {
111+
return generateDefaultAttributes(fs.constants.S_IFDIR);
112+
}
113+
const fileType = await this.getItemType(itemPath);
114+
switch (fileType) {
115+
case fs.constants.S_IFREG: {
116+
const file = await this.loadFile(itemPath);
117+
return generateAttributesForFile(file);
118+
}
119+
case fs.constants.S_IFDIR: {
120+
const folder = await this.loadFolder(itemPath);
121+
return generateAttributesForFolder(folder);
122+
}
123+
default:
124+
throw new Error('The specified path is neither a file nor a directory.');
125+
}
126+
}
127+
102128
public async loadDirectory(requestedPath: string): Promise<FileEntry[]> {
103129
if (isRootPath(requestedPath)) {
104130
return PermanentFileSystem.loadRootFileEntries();

src/classes/SftpSessionHandler.ts

Lines changed: 34 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,7 @@ import fetch from 'node-fetch';
33
import { v4 as uuidv4 } from 'uuid';
44
import ssh2 from 'ssh2';
55
import { logger } from '../logger';
6-
import {
7-
generateFileEntry,
8-
generateDefaultAttributes,
9-
generateAttributesForFile,
10-
} from '../utils';
6+
import { generateFileEntry } from '../utils';
117
import { PermanentFileSystem } from './PermanentFileSystem';
128
import type {
139
Attributes,
@@ -156,20 +152,11 @@ export class SftpSessionHandler {
156152
*/
157153
public fstatHandler = (
158154
reqId: number,
159-
handle: Buffer,
155+
itemPath: Buffer,
160156
): void => {
161157
logger.verbose('SFTP read open file statistics request (SSH_FXP_FSTAT)');
162-
logger.debug('Request:', { reqId, handle });
163-
const file = this.openFiles.get(handle.toString());
164-
if (!file) {
165-
logger.info('There is no open file associated with this handle', { reqId, handle });
166-
logger.debug('Response: Status (FAILURE)', { reqId }, SFTP_STATUS_CODE.FAILURE);
167-
this.sftpConnection.status(reqId, SFTP_STATUS_CODE.FAILURE);
168-
return;
169-
}
170-
const attrs = generateAttributesForFile(file);
171-
logger.debug('Response: Attributes', { reqId, attrs });
172-
this.sftpConnection.attrs(reqId, attrs);
158+
logger.debug('Request:', { reqId, itemPath });
159+
this.genericStatHandler(reqId, itemPath);
173160
};
174161

175162
/**
@@ -253,22 +240,13 @@ export class SftpSessionHandler {
253240
* Also: Retrieving File Attributes
254241
* https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-02#section-6.8
255242
*/
256-
public lstatHandler = (reqId: number, handle: Buffer): void => {
243+
public lstatHandler = (
244+
reqId: number,
245+
itemPath: Buffer,
246+
): void => {
257247
logger.verbose('SFTP read file statistics without following symbolic links request (SSH_FXP_LSTAT)');
258-
logger.debug('Request:', { reqId, handle });
259-
this.permanentFileSystem.getItemType(handle.toString())
260-
.then((fileType) => {
261-
const attrs = generateDefaultAttributes(fileType);
262-
logger.debug('Response:', { reqId, attrs });
263-
this.sftpConnection.attrs(
264-
reqId,
265-
attrs,
266-
);
267-
})
268-
.catch(() => {
269-
logger.debug('Response: Status (EOF)', { reqId }, SFTP_STATUS_CODE.NO_SUCH_FILE);
270-
this.sftpConnection.status(reqId, SFTP_STATUS_CODE.NO_SUCH_FILE);
271-
});
248+
logger.debug('Request:', { reqId, itemPath });
249+
this.genericStatHandler(reqId, itemPath);
272250
};
273251

274252
/**
@@ -277,22 +255,13 @@ export class SftpSessionHandler {
277255
* Also: Retrieving File Attributes
278256
* https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-02#section-6.8
279257
*/
280-
public statHandler = (reqId: number, handle: Buffer): void => {
258+
public statHandler = (
259+
reqId: number,
260+
itemPath: Buffer,
261+
): void => {
281262
logger.verbose('SFTP read file statistics following symbolic links request (SSH_FXP_STAT)');
282-
logger.debug('Request:', { reqId, handle });
283-
this.permanentFileSystem.getItemType(handle.toString())
284-
.then((fileType) => {
285-
const attrs = generateDefaultAttributes(fileType);
286-
logger.debug('Response:', { reqId, attrs });
287-
this.sftpConnection.attrs(
288-
reqId,
289-
attrs,
290-
);
291-
})
292-
.catch(() => {
293-
logger.debug('Response: Status (EOF)', { reqId }, SFTP_STATUS_CODE.NO_SUCH_FILE);
294-
this.sftpConnection.status(reqId, SFTP_STATUS_CODE.NO_SUCH_FILE);
295-
});
263+
logger.debug('Request:', { reqId, itemPath });
264+
this.genericStatHandler(reqId, itemPath);
296265
};
297266

298267
/**
@@ -327,11 +296,11 @@ export class SftpSessionHandler {
327296
logger.verbose('SFTP canonicalize path request (SSH_FXP_REALPATH)');
328297
logger.debug('Request:', { reqId, relativePath });
329298
const resolvedPath = path.resolve('/', relativePath);
330-
this.permanentFileSystem.getItemType(resolvedPath)
331-
.then((fileType) => {
299+
this.permanentFileSystem.getItemAttributes(resolvedPath)
300+
.then((attrs) => {
332301
const fileEntry = generateFileEntry(
333302
resolvedPath,
334-
generateDefaultAttributes(fileType),
303+
attrs,
335304
);
336305
const names = [fileEntry];
337306
logger.debug('Response:', { reqId, names });
@@ -397,4 +366,19 @@ export class SftpSessionHandler {
397366
public symLinkHandler = (): void => {
398367
logger.verbose('SFTP create symlink request (SSH_FXP_SYMLINK)');
399368
};
369+
370+
private readonly genericStatHandler = (reqId: number, itemPath: Buffer): void => {
371+
this.permanentFileSystem.getItemAttributes(itemPath.toString())
372+
.then((attrs) => {
373+
logger.debug('Response:', { reqId, attrs });
374+
this.sftpConnection.attrs(
375+
reqId,
376+
attrs,
377+
);
378+
})
379+
.catch(() => {
380+
logger.debug('Response: Status (NO_SUCH_FILE)', { reqId }, SFTP_STATUS_CODE.NO_SUCH_FILE);
381+
this.sftpConnection.status(reqId, SFTP_STATUS_CODE.NO_SUCH_FILE);
382+
});
383+
};
400384
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import fs from 'fs';
2+
import { generateDefaultMode } from './generateDefaultMode';
3+
import type { Attributes } from 'ssh2';
4+
import type { Folder } from '@permanentorg/sdk';
5+
6+
export const generateAttributesForFolder = (folder: Folder): Attributes => ({
7+
mode: generateDefaultMode(fs.constants.S_IFDIR),
8+
uid: 0,
9+
gid: 0,
10+
size: folder.size,
11+
atime: 0,
12+
mtime: folder.updatedAt.getTime(),
13+
});

src/utils/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export * from './generateAttributesForFile';
2+
export * from './generateAttributesForFolder';
23
export * from './generateDefaultAttributes';
34
export * from './generateFileEntriesForFolders';
45
export * from './generateFileEntriesForRecords';

0 commit comments

Comments
 (0)