0
# Stats and Permissions
1
2
File statistics, permission checking, and metadata operations with full compatibility for Node.js Stats objects. Provides comprehensive file system metadata access and permission management.
3
4
## Capabilities
5
6
### File Statistics
7
8
Get detailed information about files and directories including size, permissions, and timestamps.
9
10
```javascript { .api }
11
/**
12
* Synchronously get file statistics (follows symbolic links)
13
* @param path - File or directory path
14
* @returns Stats object with file information
15
*/
16
statSync(path: string | Buffer): Stats;
17
18
/**
19
* Synchronously get file statistics (does not follow symbolic links)
20
* @param path - File or directory path
21
* @returns Stats object (for symlinks, returns symlink stats not target stats)
22
*/
23
lstatSync(path: string | Buffer): Stats;
24
25
/**
26
* Asynchronously get file statistics (follows symbolic links)
27
* @param path - File or directory path
28
* @param callback - Completion callback with stats
29
*/
30
stat(path: string | Buffer, callback: (err?: Error, stats?: Stats) => void): void;
31
32
/**
33
* Asynchronously get file statistics (does not follow symbolic links)
34
* @param path - File or directory path
35
* @param callback - Completion callback with stats
36
*/
37
lstat(path: string | Buffer, callback: (err?: Error, stats?: Stats) => void): void;
38
39
interface Stats {
40
/** File size in bytes */
41
size: number;
42
/** File mode (permissions and type) */
43
mode: number;
44
/** User ID of file owner */
45
uid: number;
46
/** Group ID of file owner */
47
gid: number;
48
/** Device ID */
49
dev: number;
50
/** Inode number */
51
ino: number;
52
/** Number of hard links */
53
nlink: number;
54
/** Device ID (for special files) */
55
rdev: number;
56
/** Block size for I/O operations */
57
blksize: number;
58
/** Number of 512-byte blocks allocated */
59
blocks: number;
60
61
// Timestamps
62
/** Access time in milliseconds */
63
atimeMs: number;
64
/** Modification time in milliseconds */
65
mtimeMs: number;
66
/** Status change time in milliseconds */
67
ctimeMs: number;
68
/** Birth time in milliseconds */
69
birthtimeMs: number;
70
71
/** Access time as Date object */
72
atime: Date;
73
/** Modification time as Date object */
74
mtime: Date;
75
/** Status change time as Date object */
76
ctime: Date;
77
/** Birth time as Date object */
78
birthtime: Date;
79
80
// Type checking methods
81
/** Check if this is a regular file */
82
isFile(): boolean;
83
/** Check if this is a directory */
84
isDirectory(): boolean;
85
/** Check if this is a symbolic link (lstat only) */
86
isSymbolicLink(): boolean;
87
/** Check if this is a block device */
88
isBlockDevice(): boolean;
89
/** Check if this is a character device */
90
isCharacterDevice(): boolean;
91
/** Check if this is a FIFO pipe */
92
isFIFO(): boolean;
93
/** Check if this is a socket */
94
isSocket(): boolean;
95
}
96
```
97
98
**Usage Examples:**
99
100
```javascript
101
// Get file statistics
102
const stats = fs.statSync('/document.txt');
103
console.log('File size:', stats.size, 'bytes');
104
console.log('Is file:', stats.isFile());
105
console.log('Is directory:', stats.isDirectory());
106
console.log('Last modified:', stats.mtime);
107
console.log('File mode:', stats.mode.toString(8)); // Octal representation
108
109
// Check file type
110
if (stats.isFile()) {
111
console.log('This is a regular file');
112
} else if (stats.isDirectory()) {
113
console.log('This is a directory');
114
}
115
116
// Compare stat vs lstat for symlinks
117
fs.symlinkSync('/target.txt', '/link.txt');
118
const fileStats = fs.statSync('/link.txt'); // Stats of target file
119
const linkStats = fs.lstatSync('/link.txt'); // Stats of symlink itself
120
121
console.log('Target file size:', fileStats.size);
122
console.log('Link target path length:', linkStats.size);
123
console.log('Is target file:', fileStats.isFile()); // true
124
console.log('Is link symbolic:', linkStats.isSymbolicLink()); // true
125
126
// Async stats
127
fs.stat('/async-file.txt', (err, stats) => {
128
if (!err) {
129
console.log('File info:', {
130
size: stats.size,
131
modified: stats.mtime,
132
isFile: stats.isFile()
133
});
134
}
135
});
136
137
// Promise-based stats
138
const stats = await fs.promises.stat('/promise-file.txt');
139
console.log('File size:', stats.size);
140
```
141
142
### Permission Management
143
144
Change file and directory permissions with support for both numeric and symbolic modes.
145
146
```javascript { .api }
147
/**
148
* Synchronously change file permissions (follows symbolic links)
149
* @param path - File or directory path
150
* @param mode - New file mode (permissions)
151
*/
152
chmodSync(path: string | Buffer, mode: number | string): void;
153
154
/**
155
* Synchronously change file permissions (does not follow symbolic links)
156
* @param path - File or directory path
157
* @param mode - New file mode (permissions)
158
*/
159
lchmodSync(path: string | Buffer, mode: number | string): void;
160
161
/**
162
* Asynchronously change file permissions (follows symbolic links)
163
* @param path - File or directory path
164
* @param mode - New file mode (permissions)
165
* @param callback - Completion callback
166
*/
167
chmod(path: string | Buffer, mode: number | string, callback?: (err?: Error) => void): void;
168
169
/**
170
* Asynchronously change file permissions (does not follow symbolic links)
171
* @param path - File or directory path
172
* @param mode - New file mode (permissions)
173
* @param callback - Completion callback
174
*/
175
lchmod(path: string | Buffer, mode: number | string, callback?: (err?: Error) => void): void;
176
```
177
178
**Permission Modes:**
179
- Numeric: `0o755`, `0o644`, `0o600` (octal notation)
180
- String: `"755"`, `"644"`, `"600"` (octal string)
181
- Common patterns:
182
- `0o755` - rwxr-xr-x (executable file)
183
- `0o644` - rw-r--r-- (regular file)
184
- `0o600` - rw------- (private file)
185
- `0o777` - rwxrwxrwx (fully accessible)
186
187
**Usage Examples:**
188
189
```javascript
190
// Set file permissions using numeric mode
191
fs.chmodSync('/script.sh', 0o755); // Make executable
192
fs.chmodSync('/config.json', 0o644); // Read-write for owner, read-only for others
193
fs.chmodSync('/private.key', 0o600); // Owner only
194
195
// Set permissions using string mode
196
fs.chmodSync('/public.txt', '644');
197
fs.chmodSync('/secure.txt', '600');
198
199
// Change directory permissions
200
fs.chmodSync('/uploads', 0o755); // Directory with execute permission
201
202
// Use lchmod for symbolic links (change link permissions, not target)
203
fs.symlinkSync('/target.txt', '/link.txt');
204
fs.lchmodSync('/link.txt', 0o644); // Change link permissions only
205
206
// Async permission changes
207
fs.chmod('/async-file.txt', 0o755, (err) => {
208
if (!err) {
209
console.log('Permissions changed successfully');
210
}
211
});
212
213
// Promise-based permission changes
214
await fs.promises.chmod('/promise-file.txt', 0o644);
215
216
// Check permissions after changing
217
const stats = fs.statSync('/script.sh');
218
console.log('File mode:', (stats.mode & 0o777).toString(8)); // Get permission bits
219
```
220
221
### File Renaming and Moving
222
223
Rename or move files and directories within the filesystem.
224
225
```javascript { .api }
226
/**
227
* Synchronously rename/move a file or directory
228
* @param oldPath - Current path
229
* @param newPath - New path
230
*/
231
renameSync(oldPath: string | Buffer, newPath: string | Buffer): void;
232
233
/**
234
* Asynchronously rename/move a file or directory
235
* @param oldPath - Current path
236
* @param newPath - New path
237
* @param callback - Completion callback
238
*/
239
rename(oldPath: string | Buffer, newPath: string | Buffer, callback?: (err?: Error) => void): void;
240
```
241
242
**Usage Examples:**
243
244
```javascript
245
// Rename a file
246
fs.writeFileSync('/old-name.txt', 'content');
247
fs.renameSync('/old-name.txt', '/new-name.txt');
248
249
// Move file to different directory
250
fs.mkdirSync('/archive');
251
fs.renameSync('/document.txt', '/archive/document.txt');
252
253
// Rename directory
254
fs.mkdirSync('/old-folder');
255
fs.renameSync('/old-folder', '/new-folder');
256
257
// Move and rename simultaneously
258
fs.renameSync('/src/temp.js', '/build/compiled.js');
259
260
// Async rename
261
fs.rename('/temp.txt', '/permanent.txt', (err) => {
262
if (!err) {
263
console.log('File renamed successfully');
264
}
265
});
266
267
// Promise-based rename
268
await fs.promises.rename('/source.txt', '/destination.txt');
269
270
// Safe rename with existence check
271
function safeRename(oldPath, newPath) {
272
if (!fs.existsSync(oldPath)) {
273
throw new Error(`Source file does not exist: ${oldPath}`);
274
}
275
276
if (fs.existsSync(newPath)) {
277
throw new Error(`Destination already exists: ${newPath}`);
278
}
279
280
fs.renameSync(oldPath, newPath);
281
}
282
```
283
284
### Permission Constants and Checking
285
286
Work with file permission constants and check specific permissions.
287
288
```javascript { .api }
289
// File mode constants (available via fs.constants)
290
interface FileConstants {
291
// File type constants
292
S_IFMT: number; // File type mask
293
S_IFREG: number; // Regular file
294
S_IFDIR: number; // Directory
295
S_IFLNK: number; // Symbolic link
296
S_IFCHR: number; // Character device
297
S_IFBLK: number; // Block device
298
S_IFIFO: number; // FIFO pipe
299
S_IFSOCK: number; // Socket
300
301
// Permission constants
302
S_IRWXU: number; // User read, write, execute (0o700)
303
S_IRUSR: number; // User read (0o400)
304
S_IWUSR: number; // User write (0o200)
305
S_IXUSR: number; // User execute (0o100)
306
307
S_IRWXG: number; // Group read, write, execute (0o070)
308
S_IRGRP: number; // Group read (0o040)
309
S_IWGRP: number; // Group write (0o020)
310
S_IXGRP: number; // Group execute (0o010)
311
312
S_IRWXO: number; // Other read, write, execute (0o007)
313
S_IROTH: number; // Other read (0o004)
314
S_IWOTH: number; // Other write (0o002)
315
S_IXOTH: number; // Other execute (0o001)
316
}
317
```
318
319
**Usage Examples:**
320
321
```javascript
322
// Check specific permissions using constants
323
const stats = fs.statSync('/file.txt');
324
const mode = stats.mode;
325
326
// Check if user can read
327
if (mode & fs.constants.S_IRUSR) {
328
console.log('User can read');
329
}
330
331
// Check if group can write
332
if (mode & fs.constants.S_IWGRP) {
333
console.log('Group can write');
334
}
335
336
// Check if others can execute
337
if (mode & fs.constants.S_IXOTH) {
338
console.log('Others can execute');
339
}
340
341
// Extract permission bits
342
const permissions = mode & 0o777;
343
console.log('Permissions:', permissions.toString(8));
344
345
// Check file type using constants
346
const fileType = mode & fs.constants.S_IFMT;
347
if (fileType === fs.constants.S_IFREG) {
348
console.log('Regular file');
349
} else if (fileType === fs.constants.S_IFDIR) {
350
console.log('Directory');
351
} else if (fileType === fs.constants.S_IFLNK) {
352
console.log('Symbolic link');
353
}
354
355
// Permission checker utility
356
function checkPermissions(path) {
357
const stats = fs.statSync(path);
358
const mode = stats.mode;
359
360
return {
361
isReadable: !!(mode & fs.constants.S_IRUSR),
362
isWritable: !!(mode & fs.constants.S_IWUSR),
363
isExecutable: !!(mode & fs.constants.S_IXUSR),
364
permissions: (mode & 0o777).toString(8),
365
type: stats.isFile() ? 'file' :
366
stats.isDirectory() ? 'directory' :
367
stats.isSymbolicLink() ? 'symlink' : 'other'
368
};
369
}
370
371
const info = checkPermissions('/script.sh');
372
console.log('File info:', info);
373
```
374
375
### Advanced Statistics Operations
376
377
Advanced patterns for working with file statistics and metadata.
378
379
```javascript
380
// Compare file timestamps
381
function compareFiles(path1, path2) {
382
const stats1 = fs.statSync(path1);
383
const stats2 = fs.statSync(path2);
384
385
return {
386
newerFile: stats1.mtime > stats2.mtime ? path1 : path2,
387
sizeDifference: stats1.size - stats2.size,
388
sameSize: stats1.size === stats2.size,
389
sameMtime: stats1.mtime.getTime() === stats2.mtime.getTime()
390
};
391
}
392
393
// Find files by size
394
function findFilesBySize(directory, minSize, maxSize) {
395
const files = [];
396
const entries = fs.readdirSync(directory, { withFileTypes: true });
397
398
for (const entry of entries) {
399
const fullPath = `${directory}/${entry.name}`;
400
401
if (entry.isFile()) {
402
const stats = fs.statSync(fullPath);
403
if (stats.size >= minSize && stats.size <= maxSize) {
404
files.push({
405
path: fullPath,
406
size: stats.size
407
});
408
}
409
} else if (entry.isDirectory()) {
410
files.push(...findFilesBySize(fullPath, minSize, maxSize));
411
}
412
}
413
414
return files;
415
}
416
417
// Get directory size recursively
418
function getDirectorySize(dirPath) {
419
let totalSize = 0;
420
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
421
422
for (const entry of entries) {
423
const fullPath = `${dirPath}/${entry.name}`;
424
425
if (entry.isFile()) {
426
const stats = fs.statSync(fullPath);
427
totalSize += stats.size;
428
} else if (entry.isDirectory()) {
429
totalSize += getDirectorySize(fullPath);
430
}
431
}
432
433
return totalSize;
434
}
435
436
// File age utilities
437
function getFileAge(path) {
438
const stats = fs.statSync(path);
439
const now = new Date();
440
const ageMs = now.getTime() - stats.mtime.getTime();
441
442
return {
443
milliseconds: ageMs,
444
seconds: Math.floor(ageMs / 1000),
445
minutes: Math.floor(ageMs / (1000 * 60)),
446
hours: Math.floor(ageMs / (1000 * 60 * 60)),
447
days: Math.floor(ageMs / (1000 * 60 * 60 * 24))
448
};
449
}
450
451
// Find old files
452
function findOldFiles(directory, daysOld) {
453
const oldFiles = [];
454
const cutoffTime = Date.now() - (daysOld * 24 * 60 * 60 * 1000);
455
456
function scanDirectory(dir) {
457
const entries = fs.readdirSync(dir, { withFileTypes: true });
458
459
for (const entry of entries) {
460
const fullPath = `${dir}/${entry.name}`;
461
462
if (entry.isFile()) {
463
const stats = fs.statSync(fullPath);
464
if (stats.mtime.getTime() < cutoffTime) {
465
oldFiles.push({
466
path: fullPath,
467
age: getFileAge(fullPath),
468
size: stats.size
469
});
470
}
471
} else if (entry.isDirectory()) {
472
scanDirectory(fullPath);
473
}
474
}
475
}
476
477
scanDirectory(directory);
478
return oldFiles;
479
}
480
```
481
482
### Permission Management Utilities
483
484
Advanced permission management and security utilities.
485
486
```javascript
487
// Set secure permissions for different file types
488
function setSecurePermissions(path) {
489
const stats = fs.statSync(path);
490
491
if (stats.isDirectory()) {
492
fs.chmodSync(path, 0o755); // rwxr-xr-x for directories
493
} else if (stats.isFile()) {
494
// Check if file should be executable
495
const content = fs.readFileSync(path, 'utf8');
496
if (content.startsWith('#!') || path.endsWith('.sh')) {
497
fs.chmodSync(path, 0o755); // rwxr-xr-x for scripts
498
} else {
499
fs.chmodSync(path, 0o644); // rw-r--r-- for regular files
500
}
501
}
502
}
503
504
// Mass permission update
505
function updatePermissionsRecursively(directory, fileMode, dirMode) {
506
const entries = fs.readdirSync(directory, { withFileTypes: true });
507
508
for (const entry of entries) {
509
const fullPath = `${directory}/${entry.name}`;
510
511
if (entry.isFile()) {
512
fs.chmodSync(fullPath, fileMode);
513
} else if (entry.isDirectory()) {
514
fs.chmodSync(fullPath, dirMode);
515
updatePermissionsRecursively(fullPath, fileMode, dirMode);
516
}
517
}
518
}
519
520
// Permission validator
521
function validatePermissions(path, expectedMode) {
522
const stats = fs.statSync(path);
523
const actualMode = stats.mode & 0o777;
524
const expected = typeof expectedMode === 'string' ?
525
parseInt(expectedMode, 8) : expectedMode;
526
527
return {
528
isValid: actualMode === expected,
529
actual: actualMode.toString(8),
530
expected: expected.toString(8),
531
needsUpdate: actualMode !== expected
532
};
533
}
534
535
// Usage examples
536
const validation = validatePermissions('/script.sh', 0o755);
537
if (!validation.isValid) {
538
console.log(`Permission mismatch: expected ${validation.expected}, got ${validation.actual}`);
539
fs.chmodSync('/script.sh', 0o755);
540
}
541
```
542
543
## Error Handling
544
545
Common statistics and permissions errors:
546
547
- `ENOENT` - File or directory doesn't exist
548
- `EPERM` - Operation not permitted (insufficient permissions)
549
- `EACCES` - Access denied
550
- `EISDIR` - Is a directory (when file expected)
551
- `ENOTDIR` - Not a directory (when directory expected)
552
553
```javascript
554
// Safe stat operation
555
function safeStat(path) {
556
try {
557
return fs.statSync(path);
558
} catch (err) {
559
if (err.code === 'ENOENT') {
560
return null; // File doesn't exist
561
}
562
throw err; // Re-throw other errors
563
}
564
}
565
566
// Safe permission change
567
function safeChmod(path, mode) {
568
try {
569
fs.chmodSync(path, mode);
570
return true;
571
} catch (err) {
572
if (err.code === 'EPERM') {
573
console.warn(`Permission denied changing mode for ${path}`);
574
return false;
575
}
576
throw err;
577
}
578
}
579
```