@@ -158,6 +158,22 @@ export const SANDBOX_PINNED_MUTATION_PYTHON = [
158158 " os.close(dir_fd)" ,
159159 " os.rmdir(basename, dir_fd=parent_fd)" ,
160160 "" ,
161+ "def entry_identity(entry_stat):" ,
162+ " return (" ,
163+ " entry_stat.st_dev," ,
164+ " entry_stat.st_ino," ,
165+ " entry_stat.st_mode," ,
166+ " entry_stat.st_size," ,
167+ " getattr(entry_stat, 'st_mtime_ns', int(entry_stat.st_mtime * 1000000000))," ,
168+ " getattr(entry_stat, 'st_ctime_ns', int(entry_stat.st_ctime * 1000000000))," ,
169+ " )" ,
170+ "" ,
171+ "def same_identity(expected, entry_stat):" ,
172+ " return expected == entry_identity(entry_stat)" ,
173+ "" ,
174+ "def source_changed_error(basename):" ,
175+ " return OSError(getattr(errno, 'ESTALE', errno.EIO), 'source changed during move fallback cleanup', basename)" ,
176+ "" ,
161177 "def copy_entry(src_parent_fd, src_basename, dst_parent_fd, dst_basename):" ,
162178 " src_stat = os.lstat(src_basename, dir_fd=src_parent_fd)" ,
163179 " if stat.S_ISDIR(src_stat.st_mode) and not stat.S_ISLNK(src_stat.st_mode):" ,
@@ -167,6 +183,7 @@ export const SANDBOX_PINNED_MUTATION_PYTHON = [
167183 " dst_dir_fd = None" ,
168184 " try:" ,
169185 " src_dir_fd = open_dir(src_basename, dir_fd=src_parent_fd)" ,
186+ " src_stat = os.fstat(src_dir_fd)" ,
170187 " dst_dir_fd = open_dir(dst_basename, dir_fd=dst_parent_fd)" ,
171188 " for child in os.listdir(src_dir_fd):" ,
172189 " copied_children.append((child, copy_entry(src_dir_fd, child, dst_dir_fd, child)))" ,
@@ -184,11 +201,11 @@ export const SANDBOX_PINNED_MUTATION_PYTHON = [
184201 " os.close(src_dir_fd)" ,
185202 " if dst_dir_fd is not None:" ,
186203 " os.close(dst_dir_fd)" ,
187- " return ('dir', copied_children)" ,
204+ " return ('dir', entry_identity(src_stat), copied_children)" ,
188205 " if stat.S_ISLNK(src_stat.st_mode):" ,
189206 " link_target = os.readlink(src_basename, dir_fd=src_parent_fd)" ,
190207 " os.symlink(link_target, dst_basename, dir_fd=dst_parent_fd)" ,
191- " return ('leaf', None)" ,
208+ " return ('leaf', entry_identity(src_stat), None)" ,
192209 " src_fd = os.open(src_basename, READ_FLAGS, dir_fd=src_parent_fd)" ,
193210 " dst_fd = None" ,
194211 " try:" ,
@@ -212,10 +229,13 @@ export const SANDBOX_PINNED_MUTATION_PYTHON = [
212229 " if dst_fd is not None:" ,
213230 " os.close(dst_fd)" ,
214231 " os.close(src_fd)" ,
215- " return ('leaf', None)" ,
232+ " return ('leaf', entry_identity(src_file_stat), None)" ,
216233 "" ,
217234 "def remove_copied_entry(parent_fd, basename, manifest):" ,
218- " kind, children = manifest" ,
235+ " kind, expected_identity, children = manifest" ,
236+ " current_stat = os.lstat(basename, dir_fd=parent_fd)" ,
237+ " if not same_identity(expected_identity, current_stat):" ,
238+ " raise source_changed_error(basename)" ,
219239 " if kind != 'dir':" ,
220240 " os.unlink(basename, dir_fd=parent_fd)" ,
221241 " return" ,
@@ -245,6 +265,7 @@ export const SANDBOX_PINNED_MUTATION_PYTHON = [
245265 " try:" ,
246266 " temp_dir_fd = open_dir(temp_dir_name, dir_fd=dst_parent_fd)" ,
247267 " src_dir_fd = open_dir(src_basename, dir_fd=src_parent_fd)" ,
268+ " src_stat = os.fstat(src_dir_fd)" ,
248269 " for child in os.listdir(src_dir_fd):" ,
249270 " copied_children.append((child, copy_entry(src_dir_fd, child, temp_dir_fd, child)))" ,
250271 " os.close(src_dir_fd)" ,
@@ -262,7 +283,7 @@ export const SANDBOX_PINNED_MUTATION_PYTHON = [
262283 " except FileNotFoundError:" ,
263284 " pass" ,
264285 " raise" ,
265- " remove_copied_entry(src_parent_fd, src_basename, ('dir', copied_children))" ,
286+ " remove_copied_entry(src_parent_fd, src_basename, ('dir', entry_identity(src_stat), copied_children))" ,
266287 " os.fsync(dst_parent_fd)" ,
267288 " os.fsync(src_parent_fd)" ,
268289 " return" ,
0 commit comments