lean_ctx/core/
cloud_files.rs1use std::path::Path;
12
13#[cfg(windows)]
19pub fn is_cloud_placeholder(path: &Path) -> bool {
20 use std::os::windows::fs::MetadataExt;
21 std::fs::symlink_metadata(path)
24 .map(|m| attrs_indicate_placeholder(m.file_attributes()))
25 .unwrap_or(false)
26}
27
28#[cfg(target_os = "macos")]
31pub fn is_cloud_placeholder(path: &Path) -> bool {
32 use std::os::unix::ffi::OsStrExt;
33 const SF_DATALESS: u32 = 0x4000_0000;
34 let Ok(cpath) = std::ffi::CString::new(path.as_os_str().as_bytes()) else {
35 return false;
36 };
37 unsafe {
40 let mut st: libc::stat = std::mem::zeroed();
41 if libc::lstat(cpath.as_ptr(), &raw mut st) != 0 {
42 return false;
43 }
44 st.st_flags & SF_DATALESS != 0
45 }
46}
47
48#[cfg(not(any(windows, target_os = "macos")))]
49pub fn is_cloud_placeholder(_path: &Path) -> bool {
50 false
51}
52
53pub fn keep_entry(entry: &ignore::DirEntry) -> bool {
56 !is_cloud_placeholder(entry.path())
57}
58
59#[cfg(any(windows, test))]
63pub(crate) fn attrs_indicate_placeholder(attrs: u32) -> bool {
64 const OFFLINE: u32 = 0x0000_1000;
65 const RECALL_ON_OPEN: u32 = 0x0004_0000;
66 const RECALL_ON_DATA_ACCESS: u32 = 0x0040_0000;
67 attrs & (OFFLINE | RECALL_ON_OPEN | RECALL_ON_DATA_ACCESS) != 0
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73
74 #[test]
75 fn placeholder_attribute_bits_are_detected() {
76 assert!(attrs_indicate_placeholder(0x0000_1000)); assert!(attrs_indicate_placeholder(0x0004_0000)); assert!(attrs_indicate_placeholder(0x0040_0000)); assert!(attrs_indicate_placeholder(0x0000_1020)); }
81
82 #[test]
83 fn normal_attribute_bits_are_not_placeholders() {
84 assert!(!attrs_indicate_placeholder(0x0000_0020)); assert!(!attrs_indicate_placeholder(0x0000_0010)); assert!(!attrs_indicate_placeholder(0x0000_0080)); assert!(!attrs_indicate_placeholder(0));
88 }
89
90 #[test]
91 fn regular_local_file_is_not_a_placeholder() {
92 let dir = tempfile::tempdir().unwrap();
93 let f = dir.path().join("local.txt");
94 std::fs::write(&f, "hello").unwrap();
95 assert!(!is_cloud_placeholder(&f));
96 }
97
98 #[test]
99 fn missing_path_is_not_a_placeholder() {
100 assert!(!is_cloud_placeholder(Path::new(
101 "/nonexistent/lean-ctx/cloud/xyz"
102 )));
103 }
104}