@@ -77,74 +77,85 @@ fn push_pattern<P: AsRef<Path>, F: Fn(&str) -> Result<Pattern, glob::PatternErro
77
77
pattern : P ,
78
78
f : F ,
79
79
) -> crate :: Result < ( ) > {
80
- let mut path: PathBuf = dunce:: simplified ( pattern. as_ref ( ) ) . components ( ) . collect ( ) ;
81
-
82
- if cfg ! ( windows) {
83
- // Canonicalize disk-relative paths before inserting into the list
84
- use std:: path:: { Component , Prefix } ;
85
- let mut components = path. components ( ) ;
86
- if let Some ( Component :: Prefix ( prefix) ) = components. next ( ) {
87
- if matches ! ( prefix. kind( ) , Prefix :: Disk ( _) | Prefix :: VerbatimDisk ( _) )
88
- && !matches ! ( components. next( ) , Some ( Component :: RootDir ) )
89
- {
90
- path = dunce:: simplified ( & path. canonicalize ( ) ?) . to_path_buf ( ) ;
80
+ // Reconstruct pattern path components with appropraite separator
81
+ // so `some\path/to/dir/**\*` would be `some/path/to/dir/**/*` on Unix
82
+ // and `some\path\to\dir\**\*` on Windows.
83
+ let path: PathBuf = pattern. as_ref ( ) . components ( ) . collect ( ) ;
84
+
85
+ // Add pattern as is to be matched with paths as is
86
+ let path_str = path. to_string_lossy ( ) ;
87
+ list. insert ( f ( & path_str) ?) ;
88
+
89
+ // On Windows, if path starts with a Prefix, try to strip it if possible
90
+ // so `\\?\C:\\SomeDir` would result in a scope of:
91
+ // - `\\?\C:\\SomeDir`
92
+ // - `C:\\SomeDir`
93
+ #[ cfg( windows) ]
94
+ {
95
+ use std:: path:: Component ;
96
+
97
+ if matches ! ( path. components( ) . next( ) , Some ( Component :: Prefix ( _) ) ) {
98
+ let simplified = dunce:: simplified ( & path) ;
99
+ let simplified_str = simplified. to_string_lossy ( ) ;
100
+ if simplified_str != path_str {
101
+ list. insert ( f ( & simplified_str) ?) ;
91
102
}
92
103
}
93
104
}
94
105
95
- list. insert ( f ( & path. to_string_lossy ( ) ) ?) ;
96
-
97
- let mut checked_path = None ;
98
-
99
- // attempt to canonicalize parents in case we have a path like `/data/user/0/appid/**`
100
- // where `**` obviously does not exist but we need to canonicalize the parent
101
- //
102
- // example: given the `/data/user/0/appid/assets/*` path,
103
- // it's a glob pattern so it won't exist (canonicalize() fails);
104
- //
105
- // the second iteration needs to check `/data/user/0/appid/assets` and save the `*` component to append later.
106
- //
107
- // if it also does not exist, a third iteration is required to check `/data/user/0/appid`
108
- // with `assets/*` as the cached value (`checked_path` variable)
109
- // on Android that gets canonicalized to `/data/data/appid` so the final value will be `/data/data/appid/assets/*`
110
- // which is the value we want to check when we execute the `is_allowed` function
111
- let canonicalized = loop {
106
+ // Add canonicalized version of the pattern or canonicalized version of its parents
107
+ // so `/data/user/0/appid/assets/*` would be canonicalized to `/data/data/appid/assets/*`
108
+ // and can then be matched against any of them.
109
+ if let Some ( p) = canonicalize_parent ( path) {
110
+ list. insert ( f ( & p. to_string_lossy ( ) ) ?) ;
111
+ }
112
+
113
+ Ok ( ( ) )
114
+ }
115
+
116
+ /// Attempt to canonicalize path or its parents in case we have a path like `/data/user/0/appid/**`
117
+ /// where `**` obviously does not exist but we need to canonicalize the parent.
118
+ ///
119
+ /// example: given the `/data/user/0/appid/assets/*` path,
120
+ /// it's a glob pattern so it won't exist (std::fs::canonicalize() fails);
121
+ ///
122
+ /// the second iteration needs to check `/data/user/0/appid/assets` and save the `*` component to append later.
123
+ ///
124
+ /// if it also does not exist, a third iteration is required to check `/data/user/0/appid`
125
+ /// with `assets/*` as the cached value (`checked_path` variable)
126
+ /// on Android that gets canonicalized to `/data/data/appid` so the final value will be `/data/data/appid/assets/*`
127
+ /// which is the value we want to check when we execute the `Scope::is_allowed` function
128
+ fn canonicalize_parent ( mut path : PathBuf ) -> Option < PathBuf > {
129
+ let mut failed_components = None ;
130
+
131
+ loop {
112
132
if let Ok ( path) = path. canonicalize ( ) {
113
- break Some ( if let Some ( p) = checked_path {
133
+ break Some ( if let Some ( p) = failed_components {
114
134
path. join ( p)
115
135
} else {
116
136
path
117
137
} ) ;
118
138
}
119
139
120
- // get the last component of the path as an OsStr
121
- let last = path. iter ( ) . next_back ( ) . map ( PathBuf :: from) ;
122
- if let Some ( mut p) = last {
123
- // remove the last component of the path
124
- // so the next iteration checks its parent
140
+ // grap the last component of the path
141
+ if let Some ( mut last) = path. iter ( ) . next_back ( ) . map ( PathBuf :: from) {
142
+ // remove the last component of the path so the next iteration checks its parent
143
+ // if there is no more parent components, we failed to canonicalize
125
144
if !path. pop ( ) {
126
145
break None ;
127
146
}
147
+
128
148
// append the already checked path to the last component
129
- if let Some ( checked_path) = & checked_path {
130
- p. push ( checked_path) ;
149
+ // to construct `<last>/<checked_path>` and saved it for next iteration
150
+ if let Some ( failed_components) = & failed_components {
151
+ last. push ( failed_components) ;
131
152
}
132
- // replace the checked path with the current value
133
- checked_path. replace ( p) ;
153
+ failed_components. replace ( last) ;
134
154
} else {
135
155
break None ;
136
156
}
137
- } ;
138
-
139
- if let Some ( p) = canonicalized {
140
- list. insert ( f ( & p. to_string_lossy ( ) ) ?) ;
141
- } else if cfg ! ( windows) && !path. to_string_lossy ( ) . starts_with ( "\\ \\ " ) {
142
- list. insert ( f ( & format ! ( "\\ \\ ?\\ {}" , path. display( ) ) ) ?) ;
143
157
}
144
-
145
- Ok ( ( ) )
146
158
}
147
-
148
159
impl Scope {
149
160
/// Creates a new scope from a [`FsScope`] configuration.
150
161
pub fn new < R : crate :: Runtime , M : crate :: Manager < R > > (
0 commit comments