Skip to content

Commit

Permalink
Compare offloaded item size and date attributes, download and overwri…
Browse files Browse the repository at this point in the history
…te if different than dst, push version
  • Loading branch information
geberl committed Jul 26, 2021
1 parent c08b9d0 commit f770738
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 37 deletions.
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,6 @@

## Know Issues

- Bug: Offloaded files are only checked for existence at dst, size/modification time is yet not taken into account
- Meaning you can have an offloaded file with a newer version at src, and the file doesn't get copied to dst, because the old version already exists there
- Limitation: Using a non-APFS formatted drive as destination is not supported
- Copying works fine, but afterwards the file attributes can't be set correctly for e.g. exFAT (probably because those attributes don't exist there?)
- HFS+ might work, untested
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
<CommandLineArguments>
<CommandLineArgument
argument = "--help"
isEnabled = "YES">
isEnabled = "NO">
</CommandLineArgument>
<CommandLineArgument
argument = "--version"
Expand All @@ -69,11 +69,11 @@
</CommandLineArgument>
<CommandLineArgument
argument = "--dry-run"
isEnabled = "YES">
isEnabled = "NO">
</CommandLineArgument>
<CommandLineArgument
argument = "--dst &quot;/Volumes/Black/icloud-documents-backup/&quot;"
isEnabled = "NO">
isEnabled = "YES">
</CommandLineArgument>
</CommandLineArguments>
</LaunchAction>
Expand Down
37 changes: 32 additions & 5 deletions icloud-backup/icloud-backup/analyze_src.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ struct srcDirStats {
var filesToDownloadAndCopy: [URLPair]
var filesToDownloadAndCopySize: Int64

var filesToDownloadAndOverwrite: [URLPair]
var filesToDownloadAndOverwriteSize: Int64

var filesToDeleteBanlist: [URL]
var filesToDeleteBanlistSize: Int64
}
Expand All @@ -43,6 +46,8 @@ func analyzeSrcDir(srcURL: URL, dstURL: URL) -> srcDirStats {
filesToOverwriteSize: 0,
filesToDownloadAndCopy: [URLPair](),
filesToDownloadAndCopySize: 0,
filesToDownloadAndOverwrite: [URLPair](),
filesToDownloadAndOverwriteSize: 0,
filesToDeleteBanlist: [URL](),
filesToDeleteBanlistSize: 0)

Expand Down Expand Up @@ -82,18 +87,39 @@ func analyzeSrcDir(srcURL: URL, dstURL: URL) -> srcDirStats {
var realDstElementURL = dstElementURL.deletingLastPathComponent()
realDstElementURL.appendPathComponent(offladedName)

let offloadedSize = getSizeOfOffloadedContent(url: srcElementURL)

if fileManager.fileExists(atPath: realDstElementURL.path, isDirectory:&isDir) {
continue
do {
let placeholderAttr = try fileManager.attributesOfItem(atPath: srcElementURL.path)
let placeholderCreationDate = placeholderAttr[FileAttributeKey.creationDate] as! Date
let placeholderModificationDate = placeholderAttr[FileAttributeKey.modificationDate] as! Date

let dstAttr = try fileManager.attributesOfItem(atPath: realDstElementURL.path)
let dstFileSize = dstAttr[FileAttributeKey.size] as! Int64
let dstCreationDate = dstAttr[FileAttributeKey.creationDate] as! Date
let dstModificationDate = dstAttr[FileAttributeKey.modificationDate] as! Date

if offloadedSize == dstFileSize && placeholderCreationDate == dstCreationDate && placeholderModificationDate == dstModificationDate{
continue
} else {
stats.filesToDownloadAndOverwrite.append(URLPair(placeholder: srcElementURL,
src: realSrcElementURL,
dst: realDstElementURL))
stats.filesToDownloadAndOverwriteSize += offloadedSize
continue
}
} catch {
// Something happened when accessing file attributes of either src or dst.
// Did not see this yet. Unclear how to proceed.
print(error.localizedDescription)
}
}

stats.filesToDownloadAndCopy.append(URLPair(placeholder: srcElementURL,
src: realSrcElementURL,
dst: realDstElementURL))

let offloadedSize = getSizeOfOffloadedContent(url: srcElementURL)
stats.filesToDownloadAndCopySize += offloadedSize
stats.fileSize += offloadedSize

continue
}

Expand Down Expand Up @@ -136,6 +162,7 @@ func analyzeSrcDir(srcURL: URL, dstURL: URL) -> srcDirStats {
} catch {
// Something happened when accessing file attributes of either src or dst.
// Did not see this yet. Unclear how to proceed.
print(error.localizedDescription)
}
}

Expand Down
13 changes: 13 additions & 0 deletions icloud-backup/icloud-backup/backup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,19 @@ func DownloadAndCopyFiles(files: [URLPair]) {
}
}

func DownloadAndOverwriteFiles(files: [URLPair]) {
for file in files {
do {
try DownloadFromCloud(placeholder: file.placeholder!, file: file.src)
try FileManager.default.removeItem(at: file.dst)
try FileManager.default.copyItem(atPath: file.src.path, toPath: file.dst.path)
try FileManager.default.evictUbiquitousItem(at: file.src)
} catch {
print(error.localizedDescription)
}
}
}

func DownloadFromCloud(placeholder: URL, file: URL) throws {
try FileManager.default.startDownloadingUbiquitousItem(at: placeholder)
do {
Expand Down
1 change: 1 addition & 0 deletions icloud-backup/icloud-backup/cli.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ struct CLI {
CopyFiles(files: srcStats.filesToCopy)
OverwriteFiles(files: srcStats.filesToOverwrite)
DownloadAndCopyFiles(files: srcStats.filesToDownloadAndCopy)
DownloadAndOverwriteFiles(files: srcStats.filesToDownloadAndOverwrite)
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion icloud-backup/icloud-backup/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ struct IcloudbackupOptions: ParsableArguments {
// --help is automatically included
}

let cli = CLI(options: IcloudbackupOptions.parseOrExit(), version: "1.0.0")
let cli = CLI(options: IcloudbackupOptions.parseOrExit(), version: "1.1.0")

do {
try cli.run()
Expand Down
59 changes: 33 additions & 26 deletions icloud-backup/icloud-backup/stats.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,75 +35,82 @@ func getSizeString(byteCount: Int64) -> String {
}

func printDstStats(stats: dstDirStats, verbose: Bool) {
print("Results destination dir ------------------------------")
print(" start ", stats.start)
print(" end ", stats.end ?? "n/a")
print(" directories ", stats.dirCount)
print(" files ", stats.fileCount)
print(" size ", getSizeString(byteCount: stats.fileSize))
print(" directories to delete ", stats.dirsToDelete.count)
print("Results destination dir ----------------------------------")
print(" start ", stats.start)
print(" end ", stats.end ?? "n/a")
print(" directories ", stats.dirCount)
print(" files ", stats.fileCount)
print(" size ", getSizeString(byteCount: stats.fileSize))
print(" directories to delete ", stats.dirsToDelete.count)
if verbose {
for item in stats.dirsToDelete {
print(" ", item.path)
}
}
print(" files to delete ", stats.filesToDelete.count)
print(" files to delete ", stats.filesToDelete.count)
if verbose {
for item in stats.filesToDelete {
print(" ", item.path)
}
}
print(" size (delete) ", getSizeString(byteCount: stats.filesToDeleteSize))
print(" files to delete (banlist) ", stats.filesToDeleteBanlist.count)
print(" size (delete) ", getSizeString(byteCount: stats.filesToDeleteSize))
print(" files to delete (banlist) ", stats.filesToDeleteBanlist.count)
if verbose {
for item in stats.filesToDeleteBanlist {
print(" ", item.path)
}
}
print(" size (banlist) ", getSizeString(byteCount: stats.filesToDeleteBanlistSize))
print(" size (banlist) ", getSizeString(byteCount: stats.filesToDeleteBanlistSize))
print("")
}

func printSrcStats(stats: srcDirStats, verbose: Bool) {
print("Results source dir -----------------------------------")
print(" start ", stats.start)
print(" end ", stats.end ?? "n/a")
print(" directories ", stats.dirCount)
print(" files ", stats.fileCount)
print(" size ", getSizeString(byteCount: stats.fileSize))
print(" directories to create ", stats.dirsToCreate.count)
print("Results source dir ---------------------------------------")
print(" start ", stats.start)
print(" end ", stats.end ?? "n/a")
print(" directories ", stats.dirCount)
print(" files ", stats.fileCount)
print(" size ", getSizeString(byteCount: stats.fileSize))
print(" directories to create ", stats.dirsToCreate.count)
if verbose {
for item in stats.dirsToCreate {
print(" ", item.path)
}
}
print(" files to copy ", stats.filesToCopy.count)
print(" files to copy ", stats.filesToCopy.count)
if verbose {
for item in stats.filesToCopy {
print(" ", item.src.path)
}
}
print(" size (copy) ", getSizeString(byteCount: stats.filesToCopySize))
print(" files to overwrite ", stats.filesToOverwrite.count)
print(" size (copy) ", getSizeString(byteCount: stats.filesToCopySize))
print(" files to overwrite ", stats.filesToOverwrite.count)
if verbose {
for item in stats.filesToOverwrite {
print(" ", item.src.path)
}
}
print(" size (overwrite) ", getSizeString(byteCount: stats.filesToOverwriteSize))
print(" files to download & copy ", stats.filesToDownloadAndCopy.count)
print(" size (overwrite) ", getSizeString(byteCount: stats.filesToOverwriteSize))
print(" files to download & copy ", stats.filesToDownloadAndCopy.count)
if verbose {
for item in stats.filesToDownloadAndCopy {
print(" ", item.src.path)
}
}
print(" size (download & copy) ", getSizeString(byteCount: stats.filesToDownloadAndCopySize))
print(" files to delete (banlist) ", stats.filesToDeleteBanlist.count)
print(" size (download & copy) ", getSizeString(byteCount: stats.filesToDownloadAndCopySize))
print(" files to download & overwrite ", stats.filesToDownloadAndOverwrite.count)
if verbose {
for item in stats.filesToDownloadAndOverwrite {
print(" ", item.src.path)
}
}
print(" size (download & overwrite) ", getSizeString(byteCount: stats.filesToDownloadAndOverwriteSize))
print(" files to delete (banlist) ", stats.filesToDeleteBanlist.count)
if verbose {
for item in stats.filesToDeleteBanlist {
print(" ", item.path)
}
}
print(" size (banlist) ", getSizeString(byteCount: stats.filesToDeleteBanlistSize))
print(" size (banlist) ", getSizeString(byteCount: stats.filesToDeleteBanlistSize))
print("")
}

0 comments on commit f770738

Please sign in to comment.