@photostructure/fs-metadata 0.6.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -1
- package/CLAUDE.md +141 -315
- package/CODE_OF_CONDUCT.md +11 -11
- package/CONTRIBUTING.md +1 -1
- package/README.md +34 -103
- package/binding.gyp +97 -22
- package/claude.sh +23 -0
- package/dist/index.cjs +51 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -0
- package/dist/index.d.mts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.mjs +51 -21
- package/dist/index.mjs.map +1 -1
- package/doc/C++_REVIEW_TODO.md +97 -25
- package/doc/GPG_RELEASE_HOWTO.md +44 -13
- package/doc/MACOS_API_REFERENCE.md +469 -0
- package/doc/SECURITY_AUDIT_2025.md +809 -0
- package/doc/SSH_RELEASE_HOWTO.md +28 -24
- package/doc/WINDOWS_API_REFERENCE.md +422 -0
- package/doc/WINDOWS_ARM64_SECURITY.md +161 -0
- package/doc/WINDOWS_DEBUG_GUIDE.md +9 -2
- package/doc/examples.md +267 -0
- package/doc/gotchas.md +297 -0
- package/doc/logo.png +0 -0
- package/doc/logo.svg +85 -0
- package/doc/macos-asan-sip-issue.md +71 -0
- package/doc/social.png +0 -0
- package/doc/social.svg +125 -0
- package/doc/windows-build.md +226 -0
- package/doc/windows-clang-tidy.md +72 -0
- package/doc/windows-memory-testing.md +108 -0
- package/doc/windows-prebuildify-arm64.md +232 -0
- package/jest.config.cjs +23 -0
- package/package.json +61 -36
- package/prebuilds/darwin-arm64/@photostructure+fs-metadata.glibc.node +0 -0
- package/prebuilds/darwin-x64/@photostructure+fs-metadata.glibc.node +0 -0
- package/prebuilds/linux-arm64/@photostructure+fs-metadata.glibc.node +0 -0
- package/prebuilds/linux-arm64/@photostructure+fs-metadata.musl.node +0 -0
- package/prebuilds/linux-x64/@photostructure+fs-metadata.glibc.node +0 -0
- package/prebuilds/linux-x64/@photostructure+fs-metadata.musl.node +0 -0
- package/prebuilds/win32-arm64/@photostructure+fs-metadata.glibc.node +0 -0
- package/prebuilds/win32-x64/@photostructure+fs-metadata.glibc.node +0 -0
- package/scripts/check-memory.ts +186 -0
- package/scripts/clang-tidy.ts +690 -99
- package/scripts/install.cjs +42 -0
- package/scripts/is-platform.mjs +1 -1
- package/scripts/macos-asan.sh +155 -0
- package/scripts/post-build.mjs +3 -3
- package/scripts/prebuild-linux-glibc.sh +12 -1
- package/scripts/prebuildify-wrapper.ts +77 -0
- package/scripts/precommit.ts +45 -20
- package/scripts/sanitizers-test.sh +1 -1
- package/src/common/volume_metadata.h +6 -0
- package/src/darwin/hidden.cpp +73 -25
- package/src/darwin/path_security.h +149 -0
- package/src/darwin/raii_utils.h +104 -4
- package/src/darwin/volume_metadata.cpp +132 -58
- package/src/darwin/volume_mount_points.cpp +80 -47
- package/src/hidden.ts +36 -13
- package/src/linux/gio_mount_points.cpp +17 -18
- package/src/linux/gio_utils.cpp +92 -37
- package/src/linux/gio_utils.h +11 -5
- package/src/linux/gio_volume_metadata.cpp +111 -48
- package/src/linux/volume_metadata.cpp +67 -4
- package/src/object.ts +1 -0
- package/src/options.ts +6 -0
- package/src/path.ts +11 -0
- package/src/remote_info.ts +5 -3
- package/src/stack_path.ts +8 -6
- package/src/string_enum.ts +1 -0
- package/src/test-utils/memory-test-core.ts +336 -0
- package/src/test-utils/memory-test-runner.ts +108 -0
- package/src/test-utils/platform.ts +46 -1
- package/src/test-utils/worker-thread-helper.cjs +154 -27
- package/src/types/native_bindings.ts +1 -1
- package/src/types/options.ts +6 -0
- package/src/windows/drive_status.h +133 -163
- package/src/windows/error_utils.h +54 -3
- package/src/windows/fs_meta.h +1 -1
- package/src/windows/hidden.cpp +60 -43
- package/src/windows/security_utils.h +250 -0
- package/src/windows/string.h +68 -11
- package/src/windows/system_volume.h +1 -1
- package/src/windows/thread_pool.h +206 -0
- package/src/windows/volume_metadata.cpp +11 -6
- package/src/windows/volume_mount_points.cpp +8 -7
- package/src/windows/windows_arch.h +39 -0
- package/scripts/check-memory.mjs +0 -123
package/doc/SSH_RELEASE_HOWTO.md
CHANGED
|
@@ -7,6 +7,7 @@ This document explains how to set up SSH commit signing for the GitHub Actions r
|
|
|
7
7
|
For professional projects, create a dedicated bot account rather than using your personal account:
|
|
8
8
|
|
|
9
9
|
### Create the Bot Account
|
|
10
|
+
|
|
10
11
|
1. Sign out of your personal GitHub account
|
|
11
12
|
2. Go to https://github.com/join
|
|
12
13
|
3. Create account with username like `photostructure-bot`
|
|
@@ -14,6 +15,7 @@ For professional projects, create a dedicated bot account rather than using your
|
|
|
14
15
|
5. Verify the email address
|
|
15
16
|
|
|
16
17
|
### Add Bot as Repository Collaborator
|
|
18
|
+
|
|
17
19
|
1. Go to your repo: `https://github.com/photostructure/fs-metadata`
|
|
18
20
|
2. Click **Settings** tab
|
|
19
21
|
3. Click **Collaborators** in the left sidebar
|
|
@@ -23,6 +25,7 @@ For professional projects, create a dedicated bot account rather than using your
|
|
|
23
25
|
7. Send invitation
|
|
24
26
|
|
|
25
27
|
### Bot Accepts Invitation
|
|
28
|
+
|
|
26
29
|
1. Sign in as the bot account
|
|
27
30
|
2. Check notifications or email for repository invitation
|
|
28
31
|
3. Accept the invitation
|
|
@@ -74,27 +77,26 @@ cat ~/.ssh/photostructure-bot-signing | clip
|
|
|
74
77
|
2. Navigate to Settings → Secrets and variables → Actions
|
|
75
78
|
3. Add these secrets:
|
|
76
79
|
|
|
77
|
-
| Secret Name
|
|
78
|
-
|
|
80
|
+
| Secret Name | Value |
|
|
81
|
+
| ----------------- | ----------------------------- |
|
|
79
82
|
| `SSH_SIGNING_KEY` | Paste the private key content |
|
|
80
|
-
| `GIT_USER_NAME`
|
|
81
|
-
| `GIT_USER_EMAIL`
|
|
82
|
-
| `NPM_TOKEN`
|
|
83
|
+
| `GIT_USER_NAME` | `photostructure-bot` |
|
|
84
|
+
| `GIT_USER_EMAIL` | `bot@photostructure.com` |
|
|
85
|
+
| `NPM_TOKEN` | Your npm authentication token |
|
|
83
86
|
|
|
84
87
|
## 4. How SSH Signing Works in Actions
|
|
85
88
|
|
|
86
|
-
The SSH signing setup uses
|
|
89
|
+
The SSH signing setup uses the [photostructure/git-ssh-signing-action](https://github.com/marketplace/actions/git-ssh-signing-action):
|
|
90
|
+
|
|
91
|
+
### Features
|
|
87
92
|
|
|
88
|
-
### setup-ssh-bot
|
|
89
93
|
- Installs the SSH private key
|
|
90
94
|
- Configures Git to use SSH signing format
|
|
91
95
|
- Sets up commit and tag signing
|
|
92
96
|
- Creates allowed signers file for verification
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
-
|
|
96
|
-
- Clears Git signing configuration
|
|
97
|
-
- Ensures no secrets remain after workflow
|
|
97
|
+
- Automatically cleans up keys and configuration after workflow
|
|
98
|
+
- Supports Linux and macOS runners
|
|
99
|
+
- Requires Git 2.34.0+
|
|
98
100
|
|
|
99
101
|
## 5. Using SSH Signing in Workflows
|
|
100
102
|
|
|
@@ -111,7 +113,7 @@ jobs:
|
|
|
111
113
|
with:
|
|
112
114
|
fetch-depth: 0
|
|
113
115
|
|
|
114
|
-
- uses:
|
|
116
|
+
- uses: photostructure/git-ssh-signing-action@v1
|
|
115
117
|
with:
|
|
116
118
|
ssh-signing-key: ${{ secrets.SSH_SIGNING_KEY }}
|
|
117
119
|
git-user-name: ${{ secrets.GIT_USER_NAME }}
|
|
@@ -122,8 +124,7 @@ jobs:
|
|
|
122
124
|
- run: npm version patch
|
|
123
125
|
- run: git push origin main --follow-tags
|
|
124
126
|
|
|
125
|
-
|
|
126
|
-
if: always()
|
|
127
|
+
# Note: Cleanup is handled automatically by the action
|
|
127
128
|
```
|
|
128
129
|
|
|
129
130
|
## 6. Testing SSH Signing
|
|
@@ -152,14 +153,14 @@ Before triggering a release:
|
|
|
152
153
|
|
|
153
154
|
## 8. Advantages of SSH Signing
|
|
154
155
|
|
|
155
|
-
| Feature
|
|
156
|
-
|
|
157
|
-
| Setup complexity
|
|
158
|
-
| Key generation
|
|
159
|
-
| Passphrase handling | Not required | Required
|
|
160
|
-
| Wrapper scripts
|
|
161
|
-
| GitHub verification | ✓ Supported
|
|
162
|
-
| Maintenance
|
|
156
|
+
| Feature | SSH Signing | GPG Signing |
|
|
157
|
+
| ------------------- | ------------ | -------------- |
|
|
158
|
+
| Setup complexity | Simple | Complex |
|
|
159
|
+
| Key generation | One command | Multiple steps |
|
|
160
|
+
| Passphrase handling | Not required | Required |
|
|
161
|
+
| Wrapper scripts | Not needed | Required |
|
|
162
|
+
| GitHub verification | ✓ Supported | ✓ Supported |
|
|
163
|
+
| Maintenance | Minimal | Higher |
|
|
163
164
|
|
|
164
165
|
## 9. Security Best Practices
|
|
165
166
|
|
|
@@ -185,19 +186,22 @@ mv ~/.ssh/photostructure-bot-signing* ~/secure-backup/
|
|
|
185
186
|
## 11. Troubleshooting
|
|
186
187
|
|
|
187
188
|
### Commits show as "Unverified"
|
|
189
|
+
|
|
188
190
|
- Ensure the SSH key is added as a **Signing Key** (not Authentication Key)
|
|
189
191
|
- Verify the email in Git config matches the GitHub account email
|
|
190
192
|
- Check that the bot account owns the key
|
|
191
193
|
|
|
192
194
|
### "error: Load key failed"
|
|
195
|
+
|
|
193
196
|
- Verify SSH_SIGNING_KEY secret contains the complete private key
|
|
194
197
|
- Check for extra newlines or spaces in the secret
|
|
195
198
|
|
|
196
199
|
### Permission denied on push
|
|
200
|
+
|
|
197
201
|
- Ensure bot account has write access to the repository
|
|
198
202
|
|
|
199
203
|
## References
|
|
200
204
|
|
|
201
205
|
- [GitHub SSH Commit Verification](https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification#ssh-commit-signature-verification)
|
|
202
206
|
- [Git SSH Signing Documentation](https://git-scm.com/docs/git-config#Documentation/git-config.txt-gpgformat)
|
|
203
|
-
- [GitHub Actions Encrypted Secrets](https://docs.github.com/en/actions/security-guides/encrypted-secrets)
|
|
207
|
+
- [GitHub Actions Encrypted Secrets](https://docs.github.com/en/actions/security-guides/encrypted-secrets)
|
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
# Windows API Reference Guide
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This document serves as a comprehensive reference for all Windows APIs used in the fs-metadata project, with links to official Microsoft documentation and best practices.
|
|
6
|
+
|
|
7
|
+
## File System APIs
|
|
8
|
+
|
|
9
|
+
### GetLogicalDriveStringsW
|
|
10
|
+
|
|
11
|
+
- **Docs**: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getlogicaldrivestringsw
|
|
12
|
+
- **Purpose**: Fills a buffer with strings that specify valid drives in the system
|
|
13
|
+
- **Security**: Buffer overflow risk if size not checked
|
|
14
|
+
- **Best Practice**: Always call with NULL buffer first to get required size
|
|
15
|
+
|
|
16
|
+
```cpp
|
|
17
|
+
DWORD size = GetLogicalDriveStringsW(0, nullptr);
|
|
18
|
+
if (size > 0) {
|
|
19
|
+
std::unique_ptr<WCHAR[]> buffer(new WCHAR[size]);
|
|
20
|
+
if (GetLogicalDriveStringsW(size, buffer.get())) {
|
|
21
|
+
// Process drives
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### GetDriveTypeW / GetDriveTypeA
|
|
27
|
+
|
|
28
|
+
- **Docs**: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getdrivetypew
|
|
29
|
+
- **Purpose**: Determines whether a disk drive is removable, fixed, CD-ROM, RAM disk, or network drive
|
|
30
|
+
- **Return Values**:
|
|
31
|
+
- `DRIVE_UNKNOWN` (0)
|
|
32
|
+
- `DRIVE_NO_ROOT_DIR` (1)
|
|
33
|
+
- `DRIVE_REMOVABLE` (2)
|
|
34
|
+
- `DRIVE_FIXED` (3)
|
|
35
|
+
- `DRIVE_REMOTE` (4)
|
|
36
|
+
- `DRIVE_CDROM` (5)
|
|
37
|
+
- `DRIVE_RAMDISK` (6)
|
|
38
|
+
|
|
39
|
+
### GetVolumeInformationW / GetVolumeInformationA
|
|
40
|
+
|
|
41
|
+
- **Docs**: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getvolumeinformationw
|
|
42
|
+
- **Purpose**: Retrieves information about the file system and volume
|
|
43
|
+
- **Parameters**:
|
|
44
|
+
```cpp
|
|
45
|
+
BOOL GetVolumeInformationW(
|
|
46
|
+
LPCWSTR lpRootPathName, // Root directory
|
|
47
|
+
LPWSTR lpVolumeNameBuffer, // Volume name buffer
|
|
48
|
+
DWORD nVolumeNameSize, // Length of name buffer
|
|
49
|
+
LPDWORD lpVolumeSerialNumber, // Volume serial number
|
|
50
|
+
LPDWORD lpMaximumComponentLength, // Max file name length
|
|
51
|
+
LPDWORD lpFileSystemFlags, // File system options
|
|
52
|
+
LPWSTR lpFileSystemNameBuffer, // File system name buffer
|
|
53
|
+
DWORD nFileSystemNameSize // Length of file system name buffer
|
|
54
|
+
);
|
|
55
|
+
```
|
|
56
|
+
- **Common Errors**:
|
|
57
|
+
- `ERROR_NOT_READY`: Drive not ready (CD/DVD)
|
|
58
|
+
- `ERROR_PATH_NOT_FOUND`: Invalid path
|
|
59
|
+
|
|
60
|
+
### GetDiskFreeSpaceExA
|
|
61
|
+
|
|
62
|
+
- **Docs**: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getdiskfreespaceexa
|
|
63
|
+
- **Purpose**: Retrieves disk space information
|
|
64
|
+
- **Thread Safety**: Can block on network drives
|
|
65
|
+
- **Parameters**:
|
|
66
|
+
```cpp
|
|
67
|
+
BOOL GetDiskFreeSpaceExA(
|
|
68
|
+
LPCSTR lpDirectoryName, // Directory name
|
|
69
|
+
PULARGE_INTEGER lpFreeBytesAvailableToCaller, // Bytes available
|
|
70
|
+
PULARGE_INTEGER lpTotalNumberOfBytes, // Total bytes
|
|
71
|
+
PULARGE_INTEGER lpTotalNumberOfFreeBytes // Free bytes
|
|
72
|
+
);
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### GetVolumeNameForVolumeMountPointW
|
|
76
|
+
|
|
77
|
+
- **Docs**: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getvolumenameforvolumemountpointw
|
|
78
|
+
- **Purpose**: Retrieves a volume GUID path for a volume mount point
|
|
79
|
+
- **Requirements**: Input path must end with backslash
|
|
80
|
+
- **Buffer Size**: 50 characters is sufficient for GUID path
|
|
81
|
+
|
|
82
|
+
```cpp
|
|
83
|
+
WCHAR volumeGUID[50];
|
|
84
|
+
if (GetVolumeNameForVolumeMountPointW(L"C:\\", volumeGUID, 50)) {
|
|
85
|
+
// volumeGUID contains \\?\Volume{GUID}\
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### FindFirstFileExA
|
|
90
|
+
|
|
91
|
+
- **Docs**: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findfirstfileexa
|
|
92
|
+
- **Purpose**: Searches a directory for files/subdirectories
|
|
93
|
+
- **Flags**:
|
|
94
|
+
- `FIND_FIRST_EX_LARGE_FETCH`: Optimize for large directories
|
|
95
|
+
- `FIND_FIRST_EX_ON_DISK_ENTRIES_ONLY`: Skip reparse points
|
|
96
|
+
- **Security**: Can follow symbolic links if not careful
|
|
97
|
+
|
|
98
|
+
### GetFileAttributesW / SetFileAttributesW
|
|
99
|
+
|
|
100
|
+
- **Docs**:
|
|
101
|
+
- https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfileattributesw
|
|
102
|
+
- https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-setfileattributesw
|
|
103
|
+
- **Purpose**: Get/set file attributes including hidden flag
|
|
104
|
+
- **Common Attributes**:
|
|
105
|
+
- `FILE_ATTRIBUTE_HIDDEN` (0x2)
|
|
106
|
+
- `FILE_ATTRIBUTE_SYSTEM` (0x4)
|
|
107
|
+
- `FILE_ATTRIBUTE_DIRECTORY` (0x10)
|
|
108
|
+
- `FILE_ATTRIBUTE_NORMAL` (0x80)
|
|
109
|
+
- **Error Handling**: Returns `INVALID_FILE_ATTRIBUTES` on error
|
|
110
|
+
|
|
111
|
+
## Network APIs
|
|
112
|
+
|
|
113
|
+
### WNetGetConnectionA
|
|
114
|
+
|
|
115
|
+
- **Docs**: https://docs.microsoft.com/en-us/windows/win32/api/winnetwk/nf-winnetwk-wnetgetconnectiona
|
|
116
|
+
- **Purpose**: Retrieves network connection for a local device
|
|
117
|
+
- **Header**: `#include <winnetwk.h>`
|
|
118
|
+
- **Library**: `-lMpr.lib`
|
|
119
|
+
- **Buffer Management**:
|
|
120
|
+
```cpp
|
|
121
|
+
DWORD bufferSize = MAX_PATH;
|
|
122
|
+
std::unique_ptr<char[]> buffer(new char[bufferSize]);
|
|
123
|
+
DWORD result = WNetGetConnectionA("Z:", buffer.get(), &bufferSize);
|
|
124
|
+
if (result == ERROR_MORE_DATA) {
|
|
125
|
+
buffer.reset(new char[bufferSize]);
|
|
126
|
+
result = WNetGetConnectionA("Z:", buffer.get(), &bufferSize);
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## String Conversion APIs
|
|
131
|
+
|
|
132
|
+
### MultiByteToWideChar
|
|
133
|
+
|
|
134
|
+
- **Docs**: https://docs.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-multibytetowidechar
|
|
135
|
+
- **Purpose**: Maps a character string to a UTF-16 wide character string
|
|
136
|
+
- **Security**: Use `MB_ERR_INVALID_CHARS` to detect invalid sequences
|
|
137
|
+
- **Integer Overflow Protection**: Always validate returned size before allocation
|
|
138
|
+
- **Pattern**:
|
|
139
|
+
|
|
140
|
+
```cpp
|
|
141
|
+
int len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
|
|
142
|
+
source, -1, nullptr, 0);
|
|
143
|
+
if (len <= 0) {
|
|
144
|
+
// Handle error
|
|
145
|
+
return L"";
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Validate size to prevent overflow
|
|
149
|
+
if (len > PATHCCH_MAX_CCH) {
|
|
150
|
+
throw std::runtime_error("String too long for conversion");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
std::wstring result(static_cast<size_t>(len - 1), L'\0');
|
|
154
|
+
int written = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
|
|
155
|
+
source, -1, &result[0], len);
|
|
156
|
+
if (written <= 0) {
|
|
157
|
+
throw std::runtime_error("Conversion failed");
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### WideCharToMultiByte
|
|
162
|
+
|
|
163
|
+
- **Docs**: https://docs.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-widechartomultibyte
|
|
164
|
+
- **Purpose**: Maps a UTF-16 wide character string to a character string
|
|
165
|
+
- **Flags**: Use `WC_ERR_INVALID_CHARS` for Vista+ to detect errors
|
|
166
|
+
- **Integer Overflow Protection**: Validate size before `size - 1` subtraction
|
|
167
|
+
- **Pattern**:
|
|
168
|
+
|
|
169
|
+
```cpp
|
|
170
|
+
int size = WideCharToMultiByte(CP_UTF8, 0, wide, -1, nullptr, 0, nullptr, nullptr);
|
|
171
|
+
|
|
172
|
+
if (size <= 0) {
|
|
173
|
+
return ""; // Conversion failed
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Check for overflow and excessive size (1MB limit recommended)
|
|
177
|
+
if (size > INT_MAX - 1 || size > 1024 * 1024) {
|
|
178
|
+
throw std::runtime_error("String conversion size exceeds reasonable limits");
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
std::string result(static_cast<size_t>(size - 1), 0);
|
|
182
|
+
int written = WideCharToMultiByte(CP_UTF8, 0, wide, -1, &result[0], size, nullptr, nullptr);
|
|
183
|
+
if (written <= 0) {
|
|
184
|
+
throw std::runtime_error("String conversion failed");
|
|
185
|
+
}
|
|
186
|
+
return result;
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Shell APIs
|
|
190
|
+
|
|
191
|
+
### SHGetFolderPathW
|
|
192
|
+
|
|
193
|
+
- **Docs**: https://docs.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetfolderpathw
|
|
194
|
+
- **Purpose**: Gets path to special folders
|
|
195
|
+
- **Common CSIDLs**:
|
|
196
|
+
- `CSIDL_WINDOWS`: Windows directory
|
|
197
|
+
- `CSIDL_SYSTEM`: System directory
|
|
198
|
+
- `CSIDL_PROGRAM_FILES`: Program Files
|
|
199
|
+
- **Buffer Size**: MAX_PATH is always sufficient
|
|
200
|
+
|
|
201
|
+
### PathCchCanonicalizeEx
|
|
202
|
+
|
|
203
|
+
- **Docs**: https://docs.microsoft.com/en-us/windows/win32/api/pathcch/nf-pathcch-pathcchcanonicalize
|
|
204
|
+
- **Purpose**: Simplifies a path by removing navigation elements with extended options
|
|
205
|
+
- **Header**: `#include <pathcch.h>`
|
|
206
|
+
- **Library**: `-lPathcch.lib`
|
|
207
|
+
- **Security**: Prevents directory traversal attacks
|
|
208
|
+
- **Long Path Support**: Use `PATHCCH_ALLOW_LONG_PATHS` flag for paths > MAX_PATH (260 chars)
|
|
209
|
+
- **Buffer Size**: `PATHCCH_MAX_CCH` (32,768 characters) supports Windows 10+ long paths
|
|
210
|
+
- **Usage**:
|
|
211
|
+
```cpp
|
|
212
|
+
wchar_t canonicalPath[PATHCCH_MAX_CCH];
|
|
213
|
+
HRESULT hr = PathCchCanonicalizeEx(
|
|
214
|
+
canonicalPath,
|
|
215
|
+
PATHCCH_MAX_CCH,
|
|
216
|
+
inputPath,
|
|
217
|
+
PATHCCH_ALLOW_LONG_PATHS // Enable long path support
|
|
218
|
+
);
|
|
219
|
+
```
|
|
220
|
+
- **Note**: Supersedes `PathCchCanonicalize` which is limited to MAX_PATH (260 chars)
|
|
221
|
+
|
|
222
|
+
## Threading APIs
|
|
223
|
+
|
|
224
|
+
### CreateThread
|
|
225
|
+
|
|
226
|
+
- **Docs**: https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createthread
|
|
227
|
+
- **Best Practice**: Always store handle for cleanup
|
|
228
|
+
- **Never**: Never use `TerminateThread` - it can corrupt process state
|
|
229
|
+
|
|
230
|
+
### WaitForSingleObject / WaitForMultipleObjects
|
|
231
|
+
|
|
232
|
+
- **Docs**: https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitforsingleobject
|
|
233
|
+
- **Timeout**: Use INFINITE carefully - prefer specific timeouts
|
|
234
|
+
- **Return Values**:
|
|
235
|
+
- `WAIT_OBJECT_0`: Success
|
|
236
|
+
- `WAIT_TIMEOUT`: Timeout elapsed
|
|
237
|
+
- `WAIT_FAILED`: Error occurred
|
|
238
|
+
|
|
239
|
+
### Critical Sections
|
|
240
|
+
|
|
241
|
+
- **Initialize**: `InitializeCriticalSection`
|
|
242
|
+
- **Enter**: `EnterCriticalSection` (blocking)
|
|
243
|
+
- **Try Enter**: `TryEnterCriticalSection` (non-blocking)
|
|
244
|
+
- **Leave**: `LeaveCriticalSection`
|
|
245
|
+
- **Delete**: `DeleteCriticalSection`
|
|
246
|
+
- **Best Practice**: Use RAII wrapper to ensure cleanup
|
|
247
|
+
|
|
248
|
+
## Memory Management
|
|
249
|
+
|
|
250
|
+
### Heap Functions
|
|
251
|
+
|
|
252
|
+
- **HeapAlloc**: Allocate memory from heap
|
|
253
|
+
- **HeapFree**: Free heap memory
|
|
254
|
+
- **HeapValidate**: Validate heap integrity
|
|
255
|
+
- **GetProcessHeap**: Get default process heap
|
|
256
|
+
|
|
257
|
+
### Debug CRT Functions (Debug builds only)
|
|
258
|
+
|
|
259
|
+
```cpp
|
|
260
|
+
#ifdef _DEBUG
|
|
261
|
+
#define _CRTDBG_MAP_ALLOC
|
|
262
|
+
#include <crtdbg.h>
|
|
263
|
+
|
|
264
|
+
// Enable leak detection
|
|
265
|
+
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
|
|
266
|
+
|
|
267
|
+
// Set allocation breakpoint
|
|
268
|
+
_CrtSetBreakAlloc(1234); // Break on allocation #1234
|
|
269
|
+
|
|
270
|
+
// Check memory state
|
|
271
|
+
_CrtCheckMemory();
|
|
272
|
+
|
|
273
|
+
// Dump leaks on exit
|
|
274
|
+
_CrtDumpMemoryLeaks();
|
|
275
|
+
#endif
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## Security Considerations
|
|
279
|
+
|
|
280
|
+
### Path Validation
|
|
281
|
+
|
|
282
|
+
1. Check for null bytes
|
|
283
|
+
2. Check for directory traversal (..)
|
|
284
|
+
3. Check for device names (CON, PRN, AUX, etc.)
|
|
285
|
+
4. Check for alternate data streams
|
|
286
|
+
5. Validate UTF-8 sequences
|
|
287
|
+
6. Use `PathCchCanonicalizeEx` with `PATHCCH_ALLOW_LONG_PATHS` for normalization
|
|
288
|
+
7. Validate path length:
|
|
289
|
+
- Legacy limit: MAX_PATH (260 characters)
|
|
290
|
+
- Windows 10+ limit: PATHCCH_MAX_CCH (32,768 wide characters)
|
|
291
|
+
- UTF-8 validation: Account for multi-byte sequences (up to 3 bytes per wide char)
|
|
292
|
+
|
|
293
|
+
### Buffer Overflow Prevention
|
|
294
|
+
|
|
295
|
+
1. Always check buffer sizes
|
|
296
|
+
2. Use safe string functions (StringCch\*)
|
|
297
|
+
3. Validate all input lengths
|
|
298
|
+
4. Use dynamic allocation when size unknown
|
|
299
|
+
5. **Integer overflow checks** for string conversions:
|
|
300
|
+
- Validate `MultiByteToWideChar`/`WideCharToMultiByte` return values
|
|
301
|
+
- Check `size > INT_MAX - 1` before subtraction
|
|
302
|
+
- Enforce reasonable size limits (e.g., 1MB for general strings)
|
|
303
|
+
- Use `static_cast<size_t>()` for allocations to prevent sign issues
|
|
304
|
+
|
|
305
|
+
### Handle Management
|
|
306
|
+
|
|
307
|
+
1. Always close handles
|
|
308
|
+
2. Use RAII wrappers
|
|
309
|
+
3. Check for INVALID_HANDLE_VALUE
|
|
310
|
+
4. Never use handles after closing
|
|
311
|
+
|
|
312
|
+
### Memory Management with Windows APIs
|
|
313
|
+
|
|
314
|
+
1. **FormatMessage with FORMAT_MESSAGE_ALLOCATE_BUFFER**:
|
|
315
|
+
- Always use RAII wrapper (LocalFreeGuard) to prevent leaks
|
|
316
|
+
- If `std::string` constructor throws, buffer must still be freed
|
|
317
|
+
- Never rely on manual `LocalFree` after potential throwing operations
|
|
318
|
+
2. **HeapAlloc/LocalAlloc**:
|
|
319
|
+
- Prefer RAII wrappers over manual free calls
|
|
320
|
+
- Use smart pointers or custom deleters for C++ integration
|
|
321
|
+
3. **Exception Safety**:
|
|
322
|
+
- Any API that allocates memory must have cleanup in destructor
|
|
323
|
+
- Never assume operations won't throw in low-memory conditions
|
|
324
|
+
|
|
325
|
+
### Thread Safety
|
|
326
|
+
|
|
327
|
+
1. Protect shared data with synchronization
|
|
328
|
+
2. Use atomic operations where appropriate
|
|
329
|
+
3. Avoid blocking operations in critical sections
|
|
330
|
+
4. Always clean up threads gracefully
|
|
331
|
+
|
|
332
|
+
## Error Handling
|
|
333
|
+
|
|
334
|
+
### Common Error Codes
|
|
335
|
+
|
|
336
|
+
- `ERROR_SUCCESS` (0): Operation successful
|
|
337
|
+
- `ERROR_FILE_NOT_FOUND` (2): File not found
|
|
338
|
+
- `ERROR_PATH_NOT_FOUND` (3): Path not found
|
|
339
|
+
- `ERROR_ACCESS_DENIED` (5): Access denied
|
|
340
|
+
- `ERROR_INVALID_HANDLE` (6): Invalid handle
|
|
341
|
+
- `ERROR_NOT_READY` (21): Device not ready
|
|
342
|
+
- `ERROR_SHARING_VIOLATION` (32): File in use
|
|
343
|
+
- `ERROR_NETWORK_ACCESS_DENIED` (65): Network access denied
|
|
344
|
+
- `ERROR_BAD_NETPATH` (53): Network path not found
|
|
345
|
+
- `ERROR_MORE_DATA` (234): More data available
|
|
346
|
+
- `ERROR_NO_MORE_FILES` (18): No more files
|
|
347
|
+
|
|
348
|
+
### FormatMessage
|
|
349
|
+
|
|
350
|
+
- **Docs**: https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessagea
|
|
351
|
+
- **Memory Management**: With `FORMAT_MESSAGE_ALLOCATE_BUFFER`, must call `LocalFree`
|
|
352
|
+
- **Exception Safety**: Use RAII wrapper to prevent leaks if exceptions thrown
|
|
353
|
+
- **Pattern**:
|
|
354
|
+
|
|
355
|
+
```cpp
|
|
356
|
+
// UNSAFE - memory leak if exception thrown:
|
|
357
|
+
LPVOID msgBuffer;
|
|
358
|
+
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | ..., &msgBuffer, ...);
|
|
359
|
+
std::string msg((LPSTR)msgBuffer); // If this throws, LocalFree never called!
|
|
360
|
+
LocalFree(msgBuffer);
|
|
361
|
+
|
|
362
|
+
// SAFE - RAII wrapper ensures cleanup:
|
|
363
|
+
struct LocalFreeGuard {
|
|
364
|
+
LPVOID ptr;
|
|
365
|
+
explicit LocalFreeGuard(LPVOID p) : ptr(p) {}
|
|
366
|
+
~LocalFreeGuard() { if (ptr) LocalFree(ptr); }
|
|
367
|
+
LocalFreeGuard(const LocalFreeGuard&) = delete;
|
|
368
|
+
LocalFreeGuard& operator=(const LocalFreeGuard&) = delete;
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
LPVOID msgBuffer = nullptr;
|
|
372
|
+
FormatMessageA(
|
|
373
|
+
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
374
|
+
FORMAT_MESSAGE_FROM_SYSTEM |
|
|
375
|
+
FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
376
|
+
NULL,
|
|
377
|
+
GetLastError(),
|
|
378
|
+
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
379
|
+
(LPSTR)&msgBuffer,
|
|
380
|
+
0,
|
|
381
|
+
NULL
|
|
382
|
+
);
|
|
383
|
+
|
|
384
|
+
LocalFreeGuard guard(msgBuffer); // RAII ensures LocalFree called
|
|
385
|
+
if (msgBuffer) {
|
|
386
|
+
std::string msg((LPSTR)msgBuffer); // Now safe - guard cleans up
|
|
387
|
+
// ... use msg
|
|
388
|
+
}
|
|
389
|
+
// guard destructor calls LocalFree automatically
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
## Testing Recommendations
|
|
393
|
+
|
|
394
|
+
### Memory Leak Detection
|
|
395
|
+
|
|
396
|
+
```bash
|
|
397
|
+
# Build debug version
|
|
398
|
+
node-gyp rebuild --debug
|
|
399
|
+
|
|
400
|
+
# Set CRT debug flags
|
|
401
|
+
set _CRTDBG_MAP_ALLOC=1
|
|
402
|
+
|
|
403
|
+
# Run with leak detection
|
|
404
|
+
node --expose-gc test.js
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
### Address Sanitizer
|
|
408
|
+
|
|
409
|
+
```bash
|
|
410
|
+
# Set ASan options
|
|
411
|
+
set ASAN_OPTIONS=halt_on_error=0:print_stats=1:check_initialization_order=1
|
|
412
|
+
|
|
413
|
+
# Run tests
|
|
414
|
+
npm test
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
### Performance Profiling
|
|
418
|
+
|
|
419
|
+
1. Use Visual Studio Performance Profiler
|
|
420
|
+
2. Enable heap profiling
|
|
421
|
+
3. Monitor handle counts in Task Manager
|
|
422
|
+
4. Use Performance Monitor for system metrics
|