@photostructure/fs-metadata 0.6.0 → 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 +11 -6
- package/CLAUDE.md +160 -136
- package/CODE_OF_CONDUCT.md +11 -11
- package/CONTRIBUTING.md +2 -2
- package/README.md +34 -84
- package/binding.gyp +98 -23
- package/claude.sh +23 -0
- package/dist/index.cjs +53 -22
- 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 +52 -21
- package/dist/index.mjs.map +1 -1
- package/{C++_REVIEW_TODO.md → doc/C++_REVIEW_TODO.md} +97 -25
- package/doc/GPG_RELEASE_HOWTO.md +505 -0
- package/doc/MACOS_API_REFERENCE.md +469 -0
- package/doc/SECURITY_AUDIT_2025.md +809 -0
- package/doc/SSH_RELEASE_HOWTO.md +207 -0
- package/doc/WINDOWS_API_REFERENCE.md +422 -0
- package/doc/WINDOWS_ARM64_SECURITY.md +161 -0
- package/doc/WINDOWS_DEBUG_GUIDE.md +96 -0
- 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 +24 -0
- package/package.json +68 -44
- 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 +832 -0
- 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 +119 -0
- package/scripts/prebuildify-wrapper.ts +77 -0
- package/scripts/precommit.ts +70 -0
- package/scripts/sanitizers-test.sh +7 -1
- package/scripts/{configure.mjs → setup-native.mjs} +4 -1
- package/src/binding.cpp +1 -1
- package/src/common/error_utils.h +0 -6
- 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/platform.ts +25 -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/benchmark-harness.ts +192 -0
- package/src/test-utils/debuglog-child.ts +30 -2
- package/src/test-utils/debuglog-enabled-child.ts +38 -8
- package/src/test-utils/jest-setup.ts +14 -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 +157 -26
- 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/scripts/clang-tidy.mjs +0 -73
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# SSH Bot Setup for GitHub Actions
|
|
2
|
+
|
|
3
|
+
This document explains how to set up SSH commit signing for the GitHub Actions release workflow. SSH signing is newer and simpler than GPG signing, with full GitHub support.
|
|
4
|
+
|
|
5
|
+
## 0. Create Bot Account (Recommended)
|
|
6
|
+
|
|
7
|
+
For professional projects, create a dedicated bot account rather than using your personal account:
|
|
8
|
+
|
|
9
|
+
### Create the Bot Account
|
|
10
|
+
|
|
11
|
+
1. Sign out of your personal GitHub account
|
|
12
|
+
2. Go to https://github.com/join
|
|
13
|
+
3. Create account with username like `photostructure-bot`
|
|
14
|
+
4. Use email: `photostructure-bot@users.noreply.github.com`
|
|
15
|
+
5. Verify the email address
|
|
16
|
+
|
|
17
|
+
### Add Bot as Repository Collaborator
|
|
18
|
+
|
|
19
|
+
1. Go to your repo: `https://github.com/photostructure/fs-metadata`
|
|
20
|
+
2. Click **Settings** tab
|
|
21
|
+
3. Click **Collaborators** in the left sidebar
|
|
22
|
+
4. Click **Add people**
|
|
23
|
+
5. Search for your bot account username
|
|
24
|
+
6. Select **Write** permission level (needed for pushes and releases)
|
|
25
|
+
7. Send invitation
|
|
26
|
+
|
|
27
|
+
### Bot Accepts Invitation
|
|
28
|
+
|
|
29
|
+
1. Sign in as the bot account
|
|
30
|
+
2. Check notifications or email for repository invitation
|
|
31
|
+
3. Accept the invitation
|
|
32
|
+
|
|
33
|
+
## 1. Generate SSH Signing Key
|
|
34
|
+
|
|
35
|
+
Generate an Ed25519 SSH key specifically for commit signing:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# Generate the key pair
|
|
39
|
+
ssh-keygen -t ed25519 -f ~/.ssh/photostructure-bot-signing -N "" -C "photostructure-bot"
|
|
40
|
+
|
|
41
|
+
# Display the public key (you'll need this for GitHub)
|
|
42
|
+
cat ~/.ssh/photostructure-bot-signing.pub
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## 2. Add SSH Key to GitHub Bot Account
|
|
46
|
+
|
|
47
|
+
**Important**: Add the key to the **bot account**, not your personal account.
|
|
48
|
+
|
|
49
|
+
1. Sign in as `photostructure-bot`
|
|
50
|
+
2. Go to Settings → SSH and GPG keys
|
|
51
|
+
3. Click **New SSH key**
|
|
52
|
+
4. **Critical**: For "Key type", select **"Signing Key"** (not "Authentication Key")
|
|
53
|
+
5. Title: `fs-metadata Release Signing Key`
|
|
54
|
+
6. Key: Paste the contents of `~/.ssh/photostructure-bot-signing.pub`
|
|
55
|
+
7. Click **Add SSH key**
|
|
56
|
+
|
|
57
|
+
## 3. Configure Repository Secrets
|
|
58
|
+
|
|
59
|
+
Add the private key to your repository secrets:
|
|
60
|
+
|
|
61
|
+
### Copy the Private Key
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# Copy private key to clipboard (macOS)
|
|
65
|
+
cat ~/.ssh/photostructure-bot-signing | pbcopy
|
|
66
|
+
|
|
67
|
+
# Copy private key to clipboard (Linux with xclip)
|
|
68
|
+
cat ~/.ssh/photostructure-bot-signing | xclip -selection clipboard
|
|
69
|
+
|
|
70
|
+
# Copy private key to clipboard (Windows with clip)
|
|
71
|
+
cat ~/.ssh/photostructure-bot-signing | clip
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Add Repository Secrets
|
|
75
|
+
|
|
76
|
+
1. Go to your repository settings
|
|
77
|
+
2. Navigate to Settings → Secrets and variables → Actions
|
|
78
|
+
3. Add these secrets:
|
|
79
|
+
|
|
80
|
+
| Secret Name | Value |
|
|
81
|
+
| ----------------- | ----------------------------- |
|
|
82
|
+
| `SSH_SIGNING_KEY` | Paste the private key content |
|
|
83
|
+
| `GIT_USER_NAME` | `photostructure-bot` |
|
|
84
|
+
| `GIT_USER_EMAIL` | `bot@photostructure.com` |
|
|
85
|
+
| `NPM_TOKEN` | Your npm authentication token |
|
|
86
|
+
|
|
87
|
+
## 4. How SSH Signing Works in Actions
|
|
88
|
+
|
|
89
|
+
The SSH signing setup uses the [photostructure/git-ssh-signing-action](https://github.com/marketplace/actions/git-ssh-signing-action):
|
|
90
|
+
|
|
91
|
+
### Features
|
|
92
|
+
|
|
93
|
+
- Installs the SSH private key
|
|
94
|
+
- Configures Git to use SSH signing format
|
|
95
|
+
- Sets up commit and tag signing
|
|
96
|
+
- Creates allowed signers file for verification
|
|
97
|
+
- Automatically cleans up keys and configuration after workflow
|
|
98
|
+
- Supports Linux and macOS runners
|
|
99
|
+
- Requires Git 2.34.0+
|
|
100
|
+
|
|
101
|
+
## 5. Using SSH Signing in Workflows
|
|
102
|
+
|
|
103
|
+
### Basic Workflow Example
|
|
104
|
+
|
|
105
|
+
```yaml
|
|
106
|
+
jobs:
|
|
107
|
+
publish:
|
|
108
|
+
runs-on: ubuntu-latest
|
|
109
|
+
permissions:
|
|
110
|
+
contents: write
|
|
111
|
+
steps:
|
|
112
|
+
- uses: actions/checkout@v4
|
|
113
|
+
with:
|
|
114
|
+
fetch-depth: 0
|
|
115
|
+
|
|
116
|
+
- uses: photostructure/git-ssh-signing-action@v1
|
|
117
|
+
with:
|
|
118
|
+
ssh-signing-key: ${{ secrets.SSH_SIGNING_KEY }}
|
|
119
|
+
git-user-name: ${{ secrets.GIT_USER_NAME }}
|
|
120
|
+
git-user-email: ${{ secrets.GIT_USER_EMAIL }}
|
|
121
|
+
|
|
122
|
+
# Your build and release steps here
|
|
123
|
+
- run: npm ci
|
|
124
|
+
- run: npm version patch
|
|
125
|
+
- run: git push origin main --follow-tags
|
|
126
|
+
|
|
127
|
+
# Note: Cleanup is handled automatically by the action
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## 6. Testing SSH Signing
|
|
131
|
+
|
|
132
|
+
Test your setup before using it in production:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
# Run the SSH signing test workflow
|
|
136
|
+
gh workflow run test-ssh-actions.yml
|
|
137
|
+
|
|
138
|
+
# Check the workflow status
|
|
139
|
+
gh run list --workflow=test-ssh-actions.yml
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## 7. Pre-Release Checklist
|
|
143
|
+
|
|
144
|
+
Before triggering a release:
|
|
145
|
+
|
|
146
|
+
- [ ] **SSH_SIGNING_KEY** secret is configured in repository
|
|
147
|
+
- [ ] **GIT_USER_NAME** and **GIT_USER_EMAIL** secrets are set
|
|
148
|
+
- [ ] **NPM_TOKEN** is valid with publish permissions
|
|
149
|
+
- [ ] SSH public key is added to bot's GitHub account as **Signing Key**
|
|
150
|
+
- [ ] Bot account has **write access** to the repository
|
|
151
|
+
- [ ] Test workflow passes: `gh workflow run test-ssh-actions.yml`
|
|
152
|
+
- [ ] You're on the main branch with latest changes
|
|
153
|
+
|
|
154
|
+
## 8. Advantages of SSH Signing
|
|
155
|
+
|
|
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 |
|
|
164
|
+
|
|
165
|
+
## 9. Security Best Practices
|
|
166
|
+
|
|
167
|
+
1. **Use Ed25519 keys**: Most secure and efficient algorithm
|
|
168
|
+
2. **Dedicated signing keys**: Don't reuse authentication keys for signing
|
|
169
|
+
3. **Bot accounts**: Use dedicated accounts for automation
|
|
170
|
+
4. **Rotate keys**: Consider rotating every 2-3 years
|
|
171
|
+
5. **Secure storage**: Never commit private keys to repositories
|
|
172
|
+
|
|
173
|
+
## 10. Cleanup
|
|
174
|
+
|
|
175
|
+
After setting up, securely remove local key copies:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
# Remove the local key files
|
|
179
|
+
rm ~/.ssh/photostructure-bot-signing
|
|
180
|
+
rm ~/.ssh/photostructure-bot-signing.pub
|
|
181
|
+
|
|
182
|
+
# Or move to secure backup location
|
|
183
|
+
mv ~/.ssh/photostructure-bot-signing* ~/secure-backup/
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## 11. Troubleshooting
|
|
187
|
+
|
|
188
|
+
### Commits show as "Unverified"
|
|
189
|
+
|
|
190
|
+
- Ensure the SSH key is added as a **Signing Key** (not Authentication Key)
|
|
191
|
+
- Verify the email in Git config matches the GitHub account email
|
|
192
|
+
- Check that the bot account owns the key
|
|
193
|
+
|
|
194
|
+
### "error: Load key failed"
|
|
195
|
+
|
|
196
|
+
- Verify SSH_SIGNING_KEY secret contains the complete private key
|
|
197
|
+
- Check for extra newlines or spaces in the secret
|
|
198
|
+
|
|
199
|
+
### Permission denied on push
|
|
200
|
+
|
|
201
|
+
- Ensure bot account has write access to the repository
|
|
202
|
+
|
|
203
|
+
## References
|
|
204
|
+
|
|
205
|
+
- [GitHub SSH Commit Verification](https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification#ssh-commit-signature-verification)
|
|
206
|
+
- [Git SSH Signing Documentation](https://git-scm.com/docs/git-config#Documentation/git-config.txt-gpgformat)
|
|
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
|