@@ -269,23 +269,8 @@ std::string normalizePathToStorageRoot(const std::string & table_location, const
269269 if (!rel_path.empty () && rel_path.front () == ' /' )
270270 rel_path = rel_path.substr (1 );
271271
272- if (!rel_path.empty () && !base.key .empty ())
273- {
274- if (rel_path == base.key || rel_path.starts_with (base.key + " /" ))
275- {
276- std::filesystem::path fs_path (rel_path);
277- std::filesystem::path normalized = fs_path.lexically_normal ();
278-
279- std::string result = normalized.string ();
280- std::replace (result.begin (), result.end (), ' \\ ' , ' /' );
281-
282- while (!result.empty () && result.front () == ' /' )
283- result = result.substr (1 );
284-
285- return result;
286- }
287- }
288-
272+ // First, try combining and normalizing to see if the path resolves outside the table location
273+ // This handles cases where paths use ".." to go up from table location to shared parent
289274 std::string combined = base.key ;
290275 if (!combined.empty () && combined.back () != ' /' )
291276 combined += ' /' ;
@@ -295,13 +280,33 @@ std::string normalizePathToStorageRoot(const std::string & table_location, const
295280 std::filesystem::path fs_path (combined);
296281 std::filesystem::path normalized = fs_path.lexically_normal ();
297282
298- std::string result = normalized.string ();
299- std::replace (result .begin (), result .end (), ' \\ ' , ' /' ); // Unlikely but some may use Windows paths
283+ std::string normalized_result = normalized.string ();
284+ std::replace (normalized_result .begin (), normalized_result .end (), ' \\ ' , ' /' );
300285
301- while (!result.empty () && result.front () == ' /' )
302- result = result.substr (1 );
286+ while (!normalized_result.empty () && normalized_result.front () == ' /' )
287+ normalized_result = normalized_result.substr (1 );
288+
289+ // Check if the normalized path still starts with base.key (meaning it's under table location)
290+ // or if it doesn't (meaning it resolved outside, e.g., via "..")
291+ if (!normalized_result.empty () && !base.key .empty ())
292+ {
293+ // If normalized path doesn't start with base.key, it means the original path
294+ // used ".." to go outside the table location - use the normalized path as-is
295+ if (!normalized_result.starts_with (base.key + " /" ) && normalized_result != base.key )
296+ {
297+ return normalized_result;
298+ }
299+
300+ // If normalized path starts with base.key, check if original path already included it
301+ // This handles cases where paths in metadata are already relative to storage root
302+ if (rel_path == base.key || rel_path.starts_with (base.key + " /" ))
303+ {
304+ // Path already includes table location, return normalized version
305+ return normalized_result;
306+ }
307+ }
303308
304- return result ;
309+ return normalized_result ;
305310}
306311
307312std::string makeAbsolutePath (const std::string & table_location, const std::string & path)
0 commit comments