public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Wolfgang Bumiller <w.bumiller@proxmox.com>
To: Lukas Wagner <l.wagner@proxmox.com>
Cc: pve-devel@lists.proxmox.com
Subject: Re: [pve-devel] [RFC proxmox 2/7] sys: add make_tmp_dir
Date: Tue, 22 Aug 2023 10:39:07 +0200	[thread overview]
Message-ID: <2rcro4k2bheh4vrr74ryju4ptzeiu4yxefhywzpyn3edlue5vf@267yvx3xhomr> (raw)
In-Reply-To: <20230821134444.620021-3-l.wagner@proxmox.com>

On Mon, Aug 21, 2023 at 03:44:39PM +0200, Lukas Wagner wrote:
> Under the hood, this function calls `mkdtemp` from libc. Unfortunatly
> the nix crate did not provide bindings for this function, so we have
> to call into libc directly.
> 
> Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
> ---
>  proxmox-sys/src/fs/dir.rs | 73 +++++++++++++++++++++++++++++++++++++--
>  1 file changed, 71 insertions(+), 2 deletions(-)
> 
> diff --git a/proxmox-sys/src/fs/dir.rs b/proxmox-sys/src/fs/dir.rs
> index 6aee316..72bf1ad 100644
> --- a/proxmox-sys/src/fs/dir.rs
> +++ b/proxmox-sys/src/fs/dir.rs
> @@ -1,6 +1,8 @@
> -use std::ffi::CStr;
> +use std::ffi::{CStr, CString, OsStr};
> +use std::fs::File;
> +use std::os::unix::ffi::OsStrExt;
>  use std::os::unix::io::{AsRawFd, OwnedFd};
> -use std::path::Path;
> +use std::path::{Path, PathBuf};
>  
>  use anyhow::{bail, Error};
>  use nix::errno::Errno;
> @@ -8,6 +10,8 @@ use nix::fcntl::OFlag;
>  use nix::sys::stat;
>  use nix::unistd;
>  
> +use proxmox_lang::try_block;
> +
>  use crate::fs::{fchown, CreateOptions};
>  
>  /// Creates directory at the provided path with specified ownership.
> @@ -152,6 +156,54 @@ fn create_path_at_do(
>      }
>  }
>  
> +///  Create a temporary directory.
> +///
> +/// `prefix` determines where the temporary directory will be created. For instance, if
> +/// `prefix` is `/tmp`, on success the function will return a path in the style of
> +/// `/tmp/tmp_XXXXXX`, where X stands for a random string, ensuring that the path is unique.
> +///
> +/// By default, the created directory has `0o700` permissions. If this is not desired, custom
> +/// [`CreateOptions`] can be passed via the `option` parameter.
> +pub fn make_tmp_dir<P: AsRef<Path>>(
> +    prefix: P,
> +    options: Option<CreateOptions>,
> +) -> Result<PathBuf, Error> {
> +    let mut template = prefix.as_ref().to_owned();
> +    template = template.join("tmp_XXXXXX");
> +    let template = CString::new(template.into_os_string().as_bytes())?;
> +
> +    let raw_template_buffer = template.into_raw();

^ This might be shorter without going over the `CString` type with just
a `Vec<u8>` with an explicit `.push(0)` without temporarily giving up
ownership for the `mkdtemp` call.

> +
> +    let path = unsafe {
> +        let raw_returned_buffer = libc::mkdtemp(raw_template_buffer);
> +        if raw_returned_buffer.is_null() {

Need to add

    let err = io::Error::last_os_error();

as the very first thing you do in this branch.

Never give any external libraries a chance to mess with your
`errno` values before you use them, even `std` ;-)

> +            // The returned pointer points to the same buffer, so in case
> +            // of an error we need to make sure to claim it back to that
> +            // it is freed properly.
> +            drop(CString::from_raw(raw_template_buffer));

^ but I think we could avoid this - but as long as you fix up the
`errno` usage the CString code can also just stay this way, no strong
feelings there.

> +            return Err(std::io::Error::last_os_error().into());
> +        }
> +        CString::from_raw(raw_returned_buffer)
> +    };
> +
> +    let path = OsStr::from_bytes(path.as_bytes());
> +    let path = PathBuf::from(path);

^ This seems like there should be a cheap non-copying version:
    PathBuf::from(OsString::from_vec(path.into_bytes())) ?

> +
> +    if let Some(options) = options {
> +        if let Err(err) = try_block!({
> +            let fd = crate::fd::open(&path, OFlag::O_DIRECTORY, stat::Mode::empty())?;
> +            let mut file = File::from(fd);
> +            options.apply_to(&mut file, &path)?;

^ Huh, just noticing this weirdness, can we fix up the apply_to API to
take a `RawFd` or `&BorrowedFd` instead of a `File`? This is... not a
file... :-) And also `CreateOptions` doesn't really need it to be
mutable ;-)

> +            Ok::<(), Error>(())
> +        }) {
> +            let _ = unistd::unlink(&path);

^ This calls `unlink(2)` which does not remove directories. You need to
use either `std::fs::remove_dir()` or `unlinkat` with
`UnlinkatFlags::RemoveDir`.

Also, please also log the error if this fails.

> +            bail!("could not apply create options to new temporary directory: {err}");
> +        }
> +    }
> +
> +    Ok(path)
> +}
> +
>  #[cfg(test)]
>  mod tests {
>      use super::*;
> @@ -169,4 +221,21 @@ mod tests {
>          )
>          .expect("expected create_path to work");
>      }
> +
> +    #[test]
> +    fn test_make_tmp_dir() -> Result<(), Error> {
> +        let options = CreateOptions::new()
> +            .owner(unistd::Uid::effective())
> +            .group(unistd::Gid::effective())
> +            .perm(stat::Mode::from_bits_truncate(0o755));
> +
> +        let path = make_tmp_dir("/tmp", Some(options))?;
> +
> +        assert!(path.exists());
> +        assert!(path.is_dir());
> +
> +        std::fs::remove_dir_all(&path)?;
> +
> +        Ok(())
> +    }
>  }
> -- 
> 2.39.2




  reply	other threads:[~2023-08-22  8:39 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-08-21 13:44 [pve-devel] [RFC storage/proxmox{, -perl-rs} 0/7] cache storage plugin status for pvestatd/API status update calls Lukas Wagner
2023-08-21 13:44 ` [pve-devel] [RFC proxmox 1/7] sys: fs: move tests to a sub-module Lukas Wagner
2023-08-30 15:38   ` [pve-devel] applied: " Thomas Lamprecht
2023-08-21 13:44 ` [pve-devel] [RFC proxmox 2/7] sys: add make_tmp_dir Lukas Wagner
2023-08-22  8:39   ` Wolfgang Bumiller [this message]
2023-08-21 13:44 ` [pve-devel] [RFC proxmox 3/7] sys: fs: remove unnecessary clippy allow directive Lukas Wagner
2023-08-21 13:44 ` [pve-devel] [RFC proxmox 4/7] cache: add new crate 'proxmox-cache' Lukas Wagner
2023-08-22 10:08   ` Max Carrara
2023-08-22 11:33     ` Lukas Wagner
2023-08-22 12:01       ` Wolfgang Bumiller
2023-08-22 11:56     ` Wolfgang Bumiller
2023-08-22 13:52       ` Max Carrara
2023-08-21 13:44 ` [pve-devel] [RFC proxmox 5/7] cache: add debian packaging Lukas Wagner
2023-08-21 13:44 ` [pve-devel] [RFC proxmox-perl-rs 6/7] cache: add bindings for `SharedCache` from `proxmox-cache` Lukas Wagner
2023-08-21 13:44 ` [pve-devel] [RFC pve-storage 7/7] stats: api: cache storage plugin status Lukas Wagner
2023-08-22  8:51   ` Lukas Wagner
2023-08-22  9:17 ` [pve-devel] [RFC storage/proxmox{, -perl-rs} 0/7] cache storage plugin status for pvestatd/API status update calls Fiona Ebner
2023-08-22 11:25   ` Wolfgang Bumiller
2023-08-30 17:07   ` Wolf Noble

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=2rcro4k2bheh4vrr74ryju4ptzeiu4yxefhywzpyn3edlue5vf@267yvx3xhomr \
    --to=w.bumiller@proxmox.com \
    --cc=l.wagner@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal