@@ -350,25 +350,43 @@ let getrandom { Cstruct.buffer; off; len } =
350350 in
351351 loop 0
352352
353- (* [with_parent_dir dir path fn] runs [fn parent (basename path)],
354- where [parent] is a path FD for [path]'s parent, resolved using [Resolve.beneath]. *)
355- let with_parent_dir op dir path fn =
353+ (* [with_parent_dir_fd dir path fn] runs [fn parent (basename path)],
354+ where [parent] is a path FD for [path]'s parent, resolved using [Resolve.beneath].
355+
356+ If [basename path] is ".." then we treat it as if path had "/." on the end,
357+ to avoid the special case.
358+
359+ todo: Optimise this by doing [fn AT_FDCWD path] if [dir = Fs].
360+ *)
361+ let with_parent_dir_fd dir path fn =
356362 let dir_path = Filename. dirname path in
357363 let leaf = Filename. basename path in
358364 Switch. run (fun sw ->
359- let parent =
360- match dir with
361- | FD d when dir_path = " ." -> d
362- | _ ->
365+ match dir with
366+ | _ when leaf = " .." ->
367+ let fd =
368+ openat ~sw ~seekable: false dir path (* Open the full path *)
369+ ~access: `R
370+ ~flags: Uring.Open_flags. (cloexec + path + directory)
371+ ~perm: 0
372+ in
373+ fn fd " ."
374+ | FD d when dir_path = " ." -> fn d leaf
375+ | _ ->
376+ let parent =
363377 openat ~sw ~seekable: false dir dir_path
364378 ~access: `R
365379 ~flags: Uring.Open_flags. (cloexec + path + directory)
366380 ~perm: 0
367- in
368- Fd. use_exn op parent @@ fun parent ->
369- fn parent leaf
381+ in
382+ fn parent leaf
370383 )
371384
385+ let with_parent_dir op dir path fn =
386+ with_parent_dir_fd dir path @@ fun parent leaf ->
387+ Fd. use_exn op parent @@ fun parent ->
388+ fn parent leaf
389+
372390let statx ?fd ~mask path buf flags =
373391 let res =
374392 match fd with
@@ -379,6 +397,23 @@ let statx ?fd ~mask path buf flags =
379397 in
380398 if res <> 0 then raise @@ Err. wrap_fs (Uring. error_of_errno res) " statx" path
381399
400+ let statx_confined ~mask ~follow fd path buf =
401+ let module X = Uring. Statx in
402+ let flags = if follow then X.Flags. empty else X.Flags. symlink_nofollow in
403+ match fd with
404+ | Fs -> statx ~mask path buf flags
405+ | Cwd | FD _ when not follow ->
406+ with_parent_dir_fd fd path @@ fun parent leaf ->
407+ statx ~mask ~fd: parent leaf buf flags
408+ | Cwd | FD _ ->
409+ Switch. run @@ fun sw ->
410+ let fd = openat ~sw ~seekable: false fd (if path = " " then " ." else path)
411+ ~access: `R
412+ ~flags: Uring.Open_flags. (cloexec + path)
413+ ~perm: 0
414+ in
415+ statx ~fd ~mask " " buf Uring.Statx.Flags. (flags + empty_path)
416+
382417let mkdir_beneath ~perm dir path =
383418 (* [mkdir] is really an operation on [path]'s parent. Get a reference to that first: *)
384419 with_parent_dir " mkdir" dir path @@ fun parent leaf ->
0 commit comments