Windows `File::delete`

Since the release of the recent security patch, the std has gained the ability to delete files by their file handle. I think it might be useful to expose this publicly. To that end I would propose a (sealed) FileExt2 trait with delete functions though I'm uncertain about how fine grained I should make this.

The issue is there are two different "semantics" for deleting files. However one of them, the more useful POSIX semantics, may not be available on older systems or for all filesystem drivers (e.g. FAT32).

Example definitions:

pub trait FileExt2: Sealed {
    /// Removes the file
    ///
    /// If successful the file will be removed as soon as the `File` is closed.
    fn posix_delete(&self) -> io::Result<()> {}

    /// Removes the file
    ///
    /// If successful the file will be flagged for removal.
    /// It will not be removed until every handle to the file is closed, system wide.
    /// In the meantime, attempts to open the file will fail with `ERROR_ACCESS_DENIED`
    fn win32_delete(&self) -> io::Result<()> {}

    /// Removes the file
    ///
    /// This will try using `posix_delete` and fallback to `win32_delete` if that's not available.
    // AKA: just try to delete the file as best you can, I don't care how.
    fn delete(&self) -> io::Result<()> {}
}

We could replace one or more of these with a delete_with_options function that instead allows setting things like semantics or whether or not to delete files with FILE_ATTRIBUTE_READONLY set, etc. Not sure if that's overkill for the standard library though.

I'm also thinking rename might be useful. And possibly some others such as "canonicalize". Basically Windows tends to operate on file handles rather than paths so any path operation often involves a file handle. Though it would be better to offer cross-platform functions if they're available.

2 Likes

Current POSIX systems also operate primarily on file descriptors rather than paths; the whole *at family of functions is designed to use file descriptors.

Sure! That's what I was thinking of with my cross platform comment. But I wasn't sure:

  • How compatible are the APIs? Can we abstract over them? Do they encompass more or less the same core functionality?
  • How cross-platform are they? Are there major platforms where important functions aren't supported?

It would be great for File to have some cross platforms fs methods for working directly with handles. But I'm uncertain if that'll work for some functions (e.g. delete works differently on Windows).

libstd already has a bunch of platform-specific Ext traits, even for basics like file size. So delete extension traits in std::os::{windows, unix} with their platform-specific functionality should be ok.

4 Likes

I think for *at we would want some kind of Dir(OwnedFd) handle, and then we could implement those as methods, e.g. Dir::open(&self, file: &Path).

1 Like

A File extension trait for deletion would not work on UNIX because a file descriptor is associated with the inode and not where it is linked in the directory tree. Windows has hard links too, so I assume the Windows handle information contains more metadata, making this possible.

The new Dir type could be abstracted over operating systems just like File is. For UNIX it would contain an OwnedFd, for Windows it would contain an OwnedHandle.

Rough API sketch:

pub struct Dir {
      inner: fs_imp::Dir,
}

impl Dir {
   fn open(dir: &Path) -> Self {}
   fn openat(&self, path: &Path, flags: &OpenatFlags) -> Result<Self> {}
   fn unlinkat(&self, path: &Path, flags: &UnlinkatFlags) -> Result<()> {}
   // ...
}

impl File {
   fn openat(dir: &Dir, file: &Path, flags: &OpenatFlags) -> Result<Self> {}
}

An example of such a Dir type which holds an owned handle and provides a fairly complete std-like public API where path arguments are relative to the handle is cap_std::fs::Dir.

2 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.