-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathRecallFiles.ps1
More file actions
194 lines (148 loc) · 7.74 KB
/
RecallFiles.ps1
File metadata and controls
194 lines (148 loc) · 7.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
param (
[string]$PathList,
[string]$RootFolder = "",
[string]$LogFile = "$(Split-Path -Parent $MyInvocation.MyCommand.Definition)\RecallFiles.log"
)
# Clear the log file if it exists
Set-Content -Path $LogFile -Value "" -ErrorAction SilentlyContinue
# Function to log messages to both console and log file
function Log-Message {
param ([string]$Message)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$logEntry = "[$timestamp] $Message"
Write-Host $logEntry
Add-Content -Path $LogFile -Value $logEntry
}
# Function to display usage information
function Show-Usage {
Log-Message "`n=== Azure File Recall Script ==="
Log-Message "Usage:"
Log-Message " powershell -File RecallFiles.ps1 -PathList 'C:\Path\To\pathlist.txt' [-RootFolder '\\Server\Share'] [-LogFile 'C:\Logs\output.log']"
Log-Message "`nParameters:"
Log-Message " -PathList : Path to the file containing filenames, folders, or wildcard patterns."
Log-Message " -RootFolder : (Optional) Root directory for files (ignored if file paths are absolute)."
Log-Message " -LogFile : (Optional) Path to log file (default: script directory)."
Log-Message "`nExamples:"
Log-Message " powershell -File RecallFiles.ps1 -PathList 'C:\Users\ExampleUser\list.txt'"
Log-Message " powershell -File RecallFiles.ps1 -PathList '\\NetworkPath\list.txt' -RootFolder '\\NetworkShare\Storage' -LogFile 'C:\Logs\output.log'"
exit 1
}
# Debug: Print input parameters
Log-Message "[DEBUG] PathList: $PathList"
Log-Message "[DEBUG] RootFolder: $RootFolder"
Log-Message "[DEBUG] LogFile: $LogFile"
# Validate input parameters
if (-not $PathList -or -not (Test-Path -LiteralPath $PathList)) {
if (-not $RootFolder) {
Log-Message "[ERROR] Either -PathList or -RootFolder must be specified."
Show-Usage
}
Log-Message "[INFO] No PathList provided. Using RootFolder as the path."
$PathEntries = @($RootFolder) # Treat RootFolder as the path
} else {
Log-Message "[INFO] Using provided PathList."
# Read the path list and clean up formatting
$PathEntries = Get-Content -Raw $PathList | Out-String | ForEach-Object { $_ -replace "`r`n|`r|`n", "`n" } | Out-String
$PathEntries = $PathEntries -split "`n" | Where-Object { $_ -match '\S' -and -not $_.StartsWith("#") } # Remove empty lines & comments
}
# Read the path list and clean up formatting
$PathEntries = Get-Content -Raw $PathList | Out-String | ForEach-Object { $_ -replace "`r`n|`r|`n", "`n" } | Out-String
$PathEntries = $PathEntries -split "`n" | Where-Object { $_ -match '\S' -and -not $_.StartsWith("#") } # Remove empty lines & comments
Log-Message "`n=== Starting Recall Process on Local Machine ===`n"
# Function to expand folders and wildcards
function Expand-Paths {
param ($Path)
# Convert Linux-style path to Windows format
$Path = $Path -replace "/", "\\"
# If RootFolder is specified and the path is relative, prepend it
if ($RootFolder -and -not [System.IO.Path]::IsPathRooted($Path)) {
$Path = Join-Path -Path $RootFolder -ChildPath $Path
}
# Debugging output
Log-Message "[DEBUG] Processing path: $Path"
# Separate base directory and wildcard (if present)
$BaseDir = [System.IO.Path]::GetDirectoryName($Path)
$Wildcard = [System.IO.Path]::GetFileName($Path)
# If it's a directory, get all files inside it recursively
if (Test-Path -LiteralPath $BaseDir -PathType Container) {
Log-Message "[DEBUG] Expanding folder: $BaseDir"
return Get-ChildItem -Path (Join-Path -Path $BaseDir -ChildPath $Wildcard) -Recurse -File | Select-Object -ExpandProperty FullName
}
# If no wildcard, return the direct path
Log-Message "[DEBUG] Using direct path: $Path"
return $Path
}
foreach ($path in $PathEntries) {
$path = $path.Trim('"').Trim() # Remove leading/trailing quotes
# Expand folders and wildcards into actual file paths
$ExpandedFiles = Expand-Paths -Path $path
foreach ($fullPath in $ExpandedFiles) {
# Convert to an absolute path
$fullPath = [System.IO.Path]::GetFullPath($fullPath)
# Convert to long UNC path format if it exceeds 260 characters
if ($fullPath.Length -ge 260) {
$fullPath = '\\\\?\\UNC' + $fullPath.Substring(1)
}
# Debugging output
Log-Message "[DEBUG] Checking file: $fullPath"
# Use Resolve-Path before Test-Path to prevent false negatives
$resolvedPath = Resolve-Path -LiteralPath $fullPath -ErrorAction SilentlyContinue
if ($resolvedPath) {
Log-Message "[INFO] File exists: $resolvedPath"
# Get file attributes
$fileItem = Get-Item -LiteralPath $resolvedPath
$fileAttributes = $fileItem.Attributes
# Decode attributes into readable format
$attributeNames = [System.Enum]::GetValues([System.IO.FileAttributes]) | Where-Object { ($fileAttributes -band $_) -eq $_ }
$attributeString = ($attributeNames -join ", ")
# Print out file attributes
Log-Message "[DEBUG] File attributes: $attributeString"
if ($fileAttributes -band [System.IO.FileAttributes]::Offline) {
Log-Message "[INFO] Tiered file detected: $resolvedPath"
# Convert to a proper Windows file path
$fsutilPath = Convert-Path -LiteralPath $resolvedPath
# Trigger recall using a partial read
Log-Message "[DEBUG] Attempting recall by reading file: $fsutilPath"
try {
$stream = [System.IO.File]::OpenRead($fsutilPath)
$buffer = New-Object byte[] 8192 # Read first 8KB instead of 4KB
#$buffer = New-Object byte[] $stream.Length # Read full file size #Read full file
$stream.Read($buffer, 0, $buffer.Length) | Out-Null
$stream.Close()
Log-Message "[INFO] Recall initiated: $fsutilPath"
} catch {
Log-Message "[ERROR] Recall failed for: $fsutilPath. Error: $_"
}
# Wait for file to fully recall
$timeout = 300 # Max wait time in seconds (5 minutes)
$elapsed = 0
while ($true) {
Start-Sleep -Seconds 10 # Add a delay between checks
$fileItem = Get-Item -LiteralPath $resolvedPath
$fileAttributes = $fileItem.Attributes
$attributeNames = [System.Enum]::GetValues([System.IO.FileAttributes]) | Where-Object { ($fileAttributes -band $_) -eq $_ }
$attributeString = ($attributeNames -join ", ")
Log-Message "[DEBUG] Checking recall status: $attributeString"
if ($fileAttributes -band [System.IO.FileAttributes]::Offline) {
Log-Message "[DEBUG] File still offline, waiting..."
} else {
Log-Message "[INFO] File successfully recalled: $fsutilPath"
break
}
$elapsed += 2 # Since we're sleeping for 2 seconds per iteration
if ($elapsed -ge $timeout) {
Log-Message "[WARNING] File recall timeout: $fsutilPath"
break
}
}
# Small delay before moving to next file (prevents throttling)
Start-Sleep -Milliseconds 500
} else {
Log-Message "[INFO] File already cached: $resolvedPath"
}
} else {
Log-Message "[WARNING] File not found: $fullPath"
}
}
}
Log-Message "`n=== Recall Process Completed ===`n"