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
//! Detection of cloud-backed placeholder files (OneDrive "Files On-Demand" on
//! Windows, iCloud Drive "dataless" files on macOS).
//!
//! Such files keep their *contents* in the cloud; merely opening or reading one
//! forces the OS to download ("hydrate") it — slow, bandwidth-/quota-hungry, and
//! on Windows it triggers OneDrive sync warnings (#363). lean-ctx must never
//! hydrate files just to index them in the background, so every directory scan
//! prunes placeholders via [`keep_entry`]. Detection is metadata-only (file
//! attributes / stat flags) and therefore never triggers a download.
use std::path::Path;
/// True if `path` is a cloud placeholder whose content is not stored locally and
/// would be downloaded on read. Metadata-only — never hydrates the file.
///
/// On platforms without an on-demand cloud-file convention this is always
/// `false`.
#[cfg(windows)]
pub fn is_cloud_placeholder(path: &Path) -> bool {
use std::os::windows::fs::MetadataExt;
// `symlink_metadata` maps to GetFileAttributes, which reports the placeholder
// attributes without recalling (downloading) the content.
std::fs::symlink_metadata(path)
.map(|m| attrs_indicate_placeholder(m.file_attributes()))
.unwrap_or(false)
}
/// macOS variant: evicted iCloud Drive files carry `SF_DATALESS` in `st_flags`.
/// `lstat` reads the flag without materialising the content.
#[cfg(target_os = "macos")]
pub fn is_cloud_placeholder(path: &Path) -> bool {
use std::os::unix::ffi::OsStrExt;
const SF_DATALESS: u32 = 0x4000_0000;
let Ok(cpath) = std::ffi::CString::new(path.as_os_str().as_bytes()) else {
return false;
};
// SAFETY: `cpath` is a valid NUL-terminated string for the call's duration;
// `st` is zero-initialised and only read after a successful `lstat`.
unsafe {
let mut st: libc::stat = std::mem::zeroed();
if libc::lstat(cpath.as_ptr(), &raw mut st) != 0 {
return false;
}
st.st_flags & SF_DATALESS != 0
}
}
#[cfg(not(any(windows, target_os = "macos")))]
pub fn is_cloud_placeholder(_path: &Path) -> bool {
false
}
/// Predicate for `ignore::WalkBuilder::filter_entry`: prune cloud placeholders so
/// a scan never descends into — or reads — an un-hydrated file or directory.
pub fn keep_entry(entry: &ignore::DirEntry) -> bool {
!is_cloud_placeholder(entry.path())
}
/// Pure check for the Windows placeholder attribute bits, extracted so it can be
/// unit-tested on every platform: `FILE_ATTRIBUTE_OFFLINE`,
/// `FILE_ATTRIBUTE_RECALL_ON_OPEN`, `FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS`.
#[cfg(any(windows, test))]
pub(crate) fn attrs_indicate_placeholder(attrs: u32) -> bool {
const OFFLINE: u32 = 0x0000_1000;
const RECALL_ON_OPEN: u32 = 0x0004_0000;
const RECALL_ON_DATA_ACCESS: u32 = 0x0040_0000;
attrs & (OFFLINE | RECALL_ON_OPEN | RECALL_ON_DATA_ACCESS) != 0
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn placeholder_attribute_bits_are_detected() {
assert!(attrs_indicate_placeholder(0x0000_1000)); // OFFLINE
assert!(attrs_indicate_placeholder(0x0004_0000)); // RECALL_ON_OPEN
assert!(attrs_indicate_placeholder(0x0040_0000)); // RECALL_ON_DATA_ACCESS
assert!(attrs_indicate_placeholder(0x0000_1020)); // OFFLINE + ARCHIVE
}
#[test]
fn normal_attribute_bits_are_not_placeholders() {
assert!(!attrs_indicate_placeholder(0x0000_0020)); // ARCHIVE
assert!(!attrs_indicate_placeholder(0x0000_0010)); // DIRECTORY
assert!(!attrs_indicate_placeholder(0x0000_0080)); // NORMAL
assert!(!attrs_indicate_placeholder(0));
}
#[test]
fn regular_local_file_is_not_a_placeholder() {
let dir = tempfile::tempdir().unwrap();
let f = dir.path().join("local.txt");
std::fs::write(&f, "hello").unwrap();
assert!(!is_cloud_placeholder(&f));
}
#[test]
fn missing_path_is_not_a_placeholder() {
assert!(!is_cloud_placeholder(Path::new(
"/nonexistent/lean-ctx/cloud/xyz"
)));
}
}