Repository: GuillaumeGomez/sysinfo (rust)

Issue:
Make `Process::root` return an `Option`
It can be a non-available information and as such, should return an `Option` rather than an empty `Path`. (For example it's empty on mac)

Pull request description:
Make `Process::exe`, `Process::cwd` and `Process::root` return an `Option<&Path>`
Fixes #1137.

Patch:
diff --git a/src/c_interface.rs b/src/c_interface.rs
index f48cff9..1cf9ba9 100644
--- a/src/c_interface.rs
+++ b/src/c_interface.rs
@@ -462,7 +462,7 @@ pub extern "C" fn sysinfo_process_executable_path(process: CProcess) -> RString
     assert!(!process.is_null());
     let process = process as *const Process;
     unsafe {
-        if let Some(p) = (*process).exe().to_str() {
+        if let Some(p) = (*process).exe().and_then(|exe| exe.to_str()) {
             if let Ok(c) = CString::new(p) {
                 return c.into_raw() as _;
             }
@@ -477,7 +477,7 @@ pub extern "C" fn sysinfo_process_root_directory(process: CProcess) -> RString {
     assert!(!process.is_null());
     let process = process as *const Process;
     unsafe {
-        if let Some(p) = (*process).root().to_str() {
+        if let Some(p) = (*process).root().and_then(|root| root.to_str()) {
             if let Ok(c) = CString::new(p) {
                 return c.into_raw() as _;
             }
@@ -492,7 +492,7 @@ pub extern "C" fn sysinfo_process_current_directory(process: CProcess) -> RStrin
     assert!(!process.is_null());
     let process = process as *const Process;
     unsafe {
-        if let Some(p) = (*process).cwd().to_str() {
+        if let Some(p) = (*process).cwd().and_then(|cwd| cwd.to_str()) {
             if let Ok(c) = CString::new(p) {
                 return c.into_raw() as _;
             }
diff --git a/src/common.rs b/src/common.rs
index d7e6ffd..385cca5 100644
--- a/src/common.rs
+++ b/src/common.rs
@@ -855,7 +855,7 @@ impl Process {
     ///
     /// let s = System::new_all();
     /// if let Some(process) = s.process(Pid::from(1337)) {
-    ///     println!("{}", process.exe().display());
+    ///     println!("{:?}", process.exe());
     /// }
     /// ```
     ///
@@ -870,7 +870,7 @@ impl Process {
     /// replacement for this.
     /// A process [may change its `cmd[0]` value](https://man7.org/linux/man-pages/man5/proc.5.html)
     /// freely, making this an untrustworthy source of information.
-    pub fn exe(&self) -> &Path {
+    pub fn exe(&self) -> Option<&Path> {
         self.inner.exe()
     }
 
@@ -909,10 +909,10 @@ impl Process {
     ///
     /// let s = System::new_all();
     /// if let Some(process) = s.process(Pid::from(1337)) {
-    ///     println!("{}", process.cwd().display());
+    ///     println!("{:?}", process.cwd());
     /// }
     /// ```
-    pub fn cwd(&self) -> &Path {
+    pub fn cwd(&self) -> Option<&Path> {
         self.inner.cwd()
     }
 
@@ -923,10 +923,10 @@ impl Process {
     ///
     /// let s = System::new_all();
     /// if let Some(process) = s.process(Pid::from(1337)) {
-    ///     println!("{}", process.root().display());
+    ///     println!("{:?}", process.root());
     /// }
     /// ```
-    pub fn root(&self) -> &Path {
+    pub fn root(&self) -> Option<&Path> {
         self.inner.root()
     }
 
diff --git a/src/unix/apple/app_store/process.rs b/src/unix/apple/app_store/process.rs
index 4ac94d8..0d90111 100644
--- a/src/unix/apple/app_store/process.rs
+++ b/src/unix/apple/app_store/process.rs
@@ -19,8 +19,8 @@ impl ProcessInner {
         &[]
     }
 
-    pub(crate) fn exe(&self) -> &Path {
-        Path::new("/")
+    pub(crate) fn exe(&self) -> Option<&Path> {
+        None
     }
 
     pub(crate) fn pid(&self) -> Pid {
@@ -31,12 +31,12 @@ impl ProcessInner {
         &[]
     }
 
-    pub(crate) fn cwd(&self) -> &Path {
-        Path::new("/")
+    pub(crate) fn cwd(&self) -> Option<&Path> {
+        None
     }
 
-    pub(crate) fn root(&self) -> &Path {
-        Path::new("/")
+    pub(crate) fn root(&self) -> Option<&Path> {
+        None
     }
 
     pub(crate) fn memory(&self) -> u64 {
diff --git a/src/unix/apple/macos/process.rs b/src/unix/apple/macos/process.rs
index b1135af..8935558 100644
--- a/src/unix/apple/macos/process.rs
+++ b/src/unix/apple/macos/process.rs
@@ -14,12 +14,12 @@ use crate::unix::utils::cstr_to_rust_with_size;
 pub(crate) struct ProcessInner {
     pub(crate) name: String,
     pub(crate) cmd: Vec<String>,
-    pub(crate) exe: PathBuf,
+    pub(crate) exe: Option<PathBuf>,
     pid: Pid,
     parent: Option<Pid>,
     pub(crate) environ: Vec<String>,
-    cwd: PathBuf,
-    pub(crate) root: PathBuf,
+    cwd: Option<PathBuf>,
+    pub(crate) root: Option<PathBuf>,
     pub(crate) memory: u64,
     pub(crate) virtual_memory: u64,
     old_utime: u64,
@@ -52,9 +52,9 @@ impl ProcessInner {
             parent: None,
             cmd: Vec::new(),
             environ: Vec::new(),
-            exe: PathBuf::new(),
-            cwd: PathBuf::new(),
-            root: PathBuf::new(),
+            exe: None,
+            cwd: None,
+            root: None,
             memory: 0,
             virtual_memory: 0,
             cpu_usage: 0.,
@@ -83,9 +83,9 @@ impl ProcessInner {
             parent,
             cmd: Vec::new(),
             environ: Vec::new(),
-            exe: PathBuf::new(),
-            cwd: PathBuf::new(),
-            root: PathBuf::new(),
+            exe: None,
+            cwd: None,
+            root: None,
             memory: 0,
             virtual_memory: 0,
             cpu_usage: 0.,
@@ -120,8 +120,8 @@ impl ProcessInner {
         &self.cmd
     }
 
-    pub(crate) fn exe(&self) -> &Path {
-        self.exe.as_path()
+    pub(crate) fn exe(&self) -> Option<&Path> {
+        self.exe.as_deref()
     }
 
     pub(crate) fn pid(&self) -> Pid {
@@ -132,12 +132,12 @@ impl ProcessInner {
         &self.environ
     }
 
-    pub(crate) fn cwd(&self) -> &Path {
-        self.cwd.as_path()
+    pub(crate) fn cwd(&self) -> Option<&Path> {
+        self.cwd.as_deref()
     }
 
-    pub(crate) fn root(&self) -> &Path {
-        self.root.as_path()
+    pub(crate) fn root(&self) -> Option<&Path> {
+        self.root.as_deref()
     }
 
     pub(crate) fn memory(&self) -> u64 {
@@ -388,9 +388,7 @@ unsafe fn get_exe_and_name_backup(
     process: &mut ProcessInner,
     refresh_kind: ProcessRefreshKind,
 ) -> bool {
-    let exe_needs_update = refresh_kind
-        .exe()
-        .needs_update(|| process.exe.as_os_str().is_empty());
+    let exe_needs_update = refresh_kind.exe().needs_update(|| process.exe.is_none());
     if !process.name.is_empty() && !exe_needs_update {
         return false;
     }
@@ -412,7 +410,7 @@ unsafe fn get_exe_and_name_backup(
                     .to_owned();
             }
             if exe_needs_update {
-                process.exe = exe;
+                process.exe = Some(exe);
             }
             true
         }
@@ -432,12 +430,8 @@ unsafe fn convert_node_path_info(node: &libc::vnode_info_path) -> Option<PathBuf
 }
 
 unsafe fn get_cwd_root(process: &mut ProcessInner, refresh_kind: ProcessRefreshKind) {
-    let cwd_needs_update = refresh_kind
-        .cwd()
-        .needs_update(|| process.cwd.as_os_str().is_empty());
-    let root_needs_update = refresh_kind
-        .root()
-        .needs_update(|| process.root.as_os_str().is_empty());
+    let cwd_needs_update = refresh_kind.cwd().needs_update(|| process.cwd.is_none());
+    let root_needs_update = refresh_kind.root().needs_update(|| process.root.is_none());
     if !cwd_needs_update && !root_needs_update {
         return;
     }
@@ -454,14 +448,10 @@ unsafe fn get_cwd_root(process: &mut ProcessInner, refresh_kind: ProcessRefreshK
         return;
     }
     if cwd_needs_update {
-        if let Some(cwd) = convert_node_path_info(&vnodepathinfo.pvi_cdir) {
-            process.cwd = cwd;
-        }
+        process.cwd = convert_node_path_info(&vnodepathinfo.pvi_cdir);
     }
     if root_needs_update {
-        if let Some(root) = convert_node_path_info(&vnodepathinfo.pvi_rdir) {
-            process.root = root;
-        }
+        process.root = convert_node_path_info(&vnodepathinfo.pvi_rdir);
     }
 }
 
@@ -557,11 +547,8 @@ unsafe fn get_process_infos(process: &mut ProcessInner, refresh_kind: ProcessRef
             .to_owned();
     }
 
-    if refresh_kind
-        .exe()
-        .needs_update(|| process.exe.as_os_str().is_empty())
-    {
-        process.exe = exe;
+    if refresh_kind.exe().needs_update(|| process.exe.is_none()) {
+        process.exe = Some(exe);
     }
 
     let environ_needs_update = refresh_kind
diff --git a/src/unix/freebsd/process.rs b/src/unix/freebsd/process.rs
index 6617f7e..1c42cd6 100644
--- a/src/unix/freebsd/process.rs
+++ b/src/unix/freebsd/process.rs
@@ -43,12 +43,12 @@ impl fmt::Display for ProcessStatus {
 pub(crate) struct ProcessInner {
     pub(crate) name: String,
     pub(crate) cmd: Vec<String>,
-    pub(crate) exe: PathBuf,
+    pub(crate) exe: Option<PathBuf>,
     pub(crate) pid: Pid,
     parent: Option<Pid>,
     pub(crate) environ: Vec<String>,
-    pub(crate) cwd: PathBuf,
-    pub(crate) root: PathBuf,
+    pub(crate) cwd: Option<PathBuf>,
+    pub(crate) root: Option<PathBuf>,
     pub(crate) memory: u64,
     pub(crate) virtual_memory: u64,
     pub(crate) updated: bool,
@@ -80,8 +80,8 @@ impl ProcessInner {
         &self.cmd
     }
 
-    pub(crate) fn exe(&self) -> &Path {
-        self.exe.as_path()
+    pub(crate) fn exe(&self) -> Option<&Path> {
+        self.exe.as_deref()
     }
 
     pub(crate) fn pid(&self) -> Pid {
@@ -92,12 +92,12 @@ impl ProcessInner {
         &self.environ
     }
 
-    pub(crate) fn cwd(&self) -> &Path {
-        self.cwd.as_path()
+    pub(crate) fn cwd(&self) -> Option<&Path> {
+        self.cwd.as_deref()
     }
 
-    pub(crate) fn root(&self) -> &Path {
-        self.root.as_path()
+    pub(crate) fn root(&self) -> Option<&Path> {
+        self.root.as_deref()
     }
 
     pub(crate) fn memory(&self) -> u64 {
@@ -277,14 +277,14 @@ pub(crate) unsafe fn get_process_data(
             virtual_memory,
             memory,
             // procstat_getfiles
-            cwd: PathBuf::new(),
-            exe: PathBuf::new(),
+            cwd: None,
+            exe: None,
             // kvm_getargv isn't thread-safe so we get it in the main thread.
             name: String::new(),
             // kvm_getargv isn't thread-safe so we get it in the main thread.
             cmd: Vec::new(),
             // kvm_getargv isn't thread-safe so we get it in the main thread.
-            root: PathBuf::new(),
+            root: None,
             // kvm_getenvv isn't thread-safe so we get it in the main thread.
             environ: Vec::new(),
             status,
@@ -297,11 +297,12 @@ pub(crate) unsafe fn get_process_data(
     }))
 }
 
-pub(crate) unsafe fn get_exe(exe: &mut PathBuf, pid: crate::Pid, refresh_kind: ProcessRefreshKind) {
-    if refresh_kind
-        .exe()
-        .needs_update(|| exe.as_os_str().is_empty())
-    {
+pub(crate) unsafe fn get_exe(
+    exe: &mut Option<PathBuf>,
+    pid: crate::Pid,
+    refresh_kind: ProcessRefreshKind,
+) {
+    if refresh_kind.exe().needs_update(|| exe.is_none()) {
         let mut buffer = [0; libc::PATH_MAX as usize + 1];
 
         *exe = get_sys_value_str(
@@ -313,10 +314,6 @@ pub(crate) unsafe fn get_exe(exe: &mut PathBuf, pid: crate::Pid, refresh_kind: P
             ],
             &mut buffer,
         )
-        .map(PathBuf::from)
-        .unwrap_or_else(|| {
-            sysinfo_debug!("Failed to get `exe` for {}", pid.0);
-            PathBuf::new()
-        });
+        .map(PathBuf::from);
     }
 }
diff --git a/src/unix/freebsd/system.rs b/src/unix/freebsd/system.rs
index 3cc5d2d..f96ca9e 100644
--- a/src/unix/freebsd/system.rs
+++ b/src/unix/freebsd/system.rs
@@ -599,12 +599,8 @@ impl SystemInfo {
         refresh_kind: ProcessRefreshKind,
     ) {
         let mut done = 0;
-        let cwd_needs_update = refresh_kind
-            .cwd()
-            .needs_update(|| proc_.cwd().as_os_str().is_empty());
-        let root_needs_update = refresh_kind
-            .root()
-            .needs_update(|| proc_.root().as_os_str().is_empty());
+        let cwd_needs_update = refresh_kind.cwd().needs_update(|| proc_.cwd().is_none());
+        let root_needs_update = refresh_kind.root().needs_update(|| proc_.root().is_none());
         if cwd_needs_update {
             done += 1;
         }
@@ -632,14 +628,14 @@ impl SystemInfo {
                 if tmp.fs_uflags & libc::PS_FST_UFLAG_CDIR != 0 {
                     if cwd_needs_update && !tmp.fs_path.is_null() {
                         if let Ok(p) = CStr::from_ptr(tmp.fs_path).to_str() {
-                            proc_.cwd = PathBuf::from(p);
+                            proc_.cwd = Some(PathBuf::from(p));
                             done -= 1;
                         }
                     }
                 } else if tmp.fs_uflags & libc::PS_FST_UFLAG_RDIR != 0 {
                     if root_needs_update && !tmp.fs_path.is_null() {
                         if let Ok(p) = CStr::from_ptr(tmp.fs_path).to_str() {
-                            proc_.root = PathBuf::from(p);
+                            proc_.root = Some(PathBuf::from(p));
                             done -= 1;
                         }
                     }
diff --git a/src/unix/linux/process.rs b/src/unix/linux/process.rs
index 2f5fd50..946b5a4 100644
--- a/src/unix/linux/process.rs
+++ b/src/unix/linux/process.rs
@@ -59,12 +59,12 @@ impl fmt::Display for ProcessStatus {
 pub(crate) struct ProcessInner {
     pub(crate) name: String,
     pub(crate) cmd: Vec<String>,
-    pub(crate) exe: PathBuf,
+    pub(crate) exe: Option<PathBuf>,
     pub(crate) pid: Pid,
     parent: Option<Pid>,
     pub(crate) environ: Vec<String>,
-    pub(crate) cwd: PathBuf,
-    pub(crate) root: PathBuf,
+    pub(crate) cwd: Option<PathBuf>,
+    pub(crate) root: Option<PathBuf>,
     pub(crate) memory: u64,
     pub(crate) virtual_memory: u64,
     utime: u64,
@@ -97,9 +97,9 @@ impl ProcessInner {
             parent: None,
             cmd: Vec::new(),
             environ: Vec::new(),
-            exe: PathBuf::new(),
-            cwd: PathBuf::new(),
-            root: PathBuf::new(),
+            exe: None,
+            cwd: None,
+            root: None,
             memory: 0,
             virtual_memory: 0,
             cpu_usage: 0.,
@@ -142,8 +142,8 @@ impl ProcessInner {
         &self.cmd
     }
 
-    pub(crate) fn exe(&self) -> &Path {
-        self.exe.as_path()
+    pub(crate) fn exe(&self) -> Option<&Path> {
+        self.exe.as_deref()
     }
 
     pub(crate) fn pid(&self) -> Pid {
@@ -154,12 +154,12 @@ impl ProcessInner {
         &self.environ
     }
 
-    pub(crate) fn cwd(&self) -> &Path {
-        self.cwd.as_path()
+    pub(crate) fn cwd(&self) -> Option<&Path> {
+        self.cwd.as_deref()
     }
 
-    pub(crate) fn root(&self) -> &Path {
-        self.root.as_path()
+    pub(crate) fn root(&self) -> Option<&Path> {
+        self.root.as_deref()
     }
 
     pub(crate) fn memory(&self) -> u64 {
@@ -378,19 +378,10 @@ fn update_proc_info(
     get_status(p, parts[2]);
     refresh_user_group_ids(p, proc_path, refresh_kind);
 
-    if refresh_kind
-        .exe()
-        .needs_update(|| p.exe.as_os_str().is_empty())
-    {
-        match proc_path.join("exe").read_link() {
-            Ok(exe_path) => p.exe = exe_path,
-            Err(_error) => {
-                sysinfo_debug!("Failed to retrieve exe for {}: {_error:?}", p.pid().0);
-                // Do not use cmd[0] because it is not the same thing.
-                // See https://github.com/GuillaumeGomez/sysinfo/issues/697.
-                p.exe = PathBuf::new();
-            }
-        }
+    if refresh_kind.exe().needs_update(|| p.exe.is_none()) {
+        // Do not use cmd[0] because it is not the same thing.
+        // See https://github.com/GuillaumeGomez/sysinfo/issues/697.
+        p.exe = realpath(proc_path.join("exe"));
     }
 
     if refresh_kind.cmd().needs_update(|| p.cmd.is_empty()) {
@@ -399,16 +390,10 @@ fn update_proc_info(
     if refresh_kind.environ().needs_update(|| p.environ.is_empty()) {
         p.environ = copy_from_file(proc_path.join("environ"));
     }
-    if refresh_kind
-        .cwd()
-        .needs_update(|| p.cwd.as_os_str().is_empty())
-    {
+    if refresh_kind.cwd().needs_update(|| p.cwd.is_none()) {
         p.cwd = realpath(proc_path.join("cwd"));
     }
-    if refresh_kind
-        .root()
-        .needs_update(|| p.root.as_os_str().is_empty())
-    {
+    if refresh_kind.root().needs_update(|| p.root.is_none()) {
         p.root = realpath(proc_path.join("root"));
     }
 
diff --git a/src/unix/linux/utils.rs b/src/unix/linux/utils.rs
index b7c62b2..7d42928 100644
--- a/src/unix/linux/utils.rs
+++ b/src/unix/linux/utils.rs
@@ -20,12 +20,12 @@ pub(crate) fn get_all_data<P: AsRef<Path>>(file_path: P, size: usize) -> io::Res
 }
 
 #[allow(clippy::useless_conversion)]
-pub(crate) fn realpath(path: &Path) -> std::path::PathBuf {
+pub(crate) fn realpath(path: &Path) -> Option<std::path::PathBuf> {
     match std::fs::read_link(path) {
-        Ok(f) => f,
+        Ok(path) => Some(path),
         Err(_e) => {
             sysinfo_debug!("failed to get real path for {:?}: {:?}", path, _e);
-            PathBuf::new()
+            None
         }
     }
 }
diff --git a/src/unknown/process.rs b/src/unknown/process.rs
index 0d6fe22..0bb4855 100644
--- a/src/unknown/process.rs
+++ b/src/unknown/process.rs
@@ -29,8 +29,8 @@ impl ProcessInner {
         &[]
     }
 
-    pub(crate) fn exe(&self) -> &Path {
-        Path::new("")
+    pub(crate) fn exe(&self) -> Option<&Path> {
+        None
     }
 
     pub(crate) fn pid(&self) -> Pid {
@@ -41,12 +41,12 @@ impl ProcessInner {
         &[]
     }
 
-    pub(crate) fn cwd(&self) -> &Path {
-        Path::new("")
+    pub(crate) fn cwd(&self) -> Option<&Path> {
+        None
     }
 
-    pub(crate) fn root(&self) -> &Path {
-        Path::new("")
+    pub(crate) fn root(&self) -> Option<&Path> {
+        None
     }
 
     pub(crate) fn memory(&self) -> u64 {
diff --git a/src/windows/process.rs b/src/windows/process.rs
index ea962f6..af63f87 100644
--- a/src/windows/process.rs
+++ b/src/windows/process.rs
@@ -201,12 +201,12 @@ unsafe impl Sync for HandleWrapper {}
 pub(crate) struct ProcessInner {
     name: String,
     cmd: Vec<String>,
-    exe: PathBuf,
+    exe: Option<PathBuf>,
     pid: Pid,
     user_id: Option<Uid>,
     environ: Vec<String>,
-    cwd: PathBuf,
-    root: PathBuf,
+    cwd: Option<PathBuf>,
+    root: Option<PathBuf>,
     pub(crate) memory: u64,
     pub(crate) virtual_memory: u64,
     parent: Option<Pid>,
@@ -338,7 +338,7 @@ unsafe fn get_process_name(pid: Pid) -> Option<String> {
     name
 }
 
-unsafe fn get_exe(process_handler: &HandleWrapper) -> PathBuf {
+unsafe fn get_exe(process_handler: &HandleWrapper) -> Option<PathBuf> {
     let mut exe_buf = [0u16; MAX_PATH as usize + 1];
     GetModuleFileNameExW(
         **process_handler,
@@ -346,7 +346,7 @@ unsafe fn get_exe(process_handler: &HandleWrapper) -> PathBuf {
         exe_buf.as_mut_slice(),
     );
 
-    PathBuf::from(null_terminated_wchar_to_string(&exe_buf))
+    Some(PathBuf::from(null_terminated_wchar_to_string(&exe_buf)))
 }
 
 impl ProcessInner {
@@ -375,7 +375,7 @@ impl ProcessInner {
             let exe = if refresh_kind.exe().needs_update(|| true) {
                 get_exe(&process_handler)
             } else {
-                PathBuf::new()
+                None
             };
             let (start_time, run_time) = get_start_and_run_time(*process_handler, now);
             let parent = if info.InheritedFromUniqueProcessId != 0 {
@@ -392,8 +392,8 @@ impl ProcessInner {
                 cmd: Vec::new(),
                 environ: Vec::new(),
                 exe,
-                cwd: PathBuf::new(),
-                root: PathBuf::new(),
+                cwd: None,
+                root: None,
                 status: ProcessStatus::Run,
                 memory: 0,
                 virtual_memory: 0,
@@ -427,7 +427,7 @@ impl ProcessInner {
                 let exe = if refresh_kind.exe().needs_update(|| true) {
                     get_exe(&handle)
                 } else {
-                    PathBuf::new()
+                    None
                 };
                 let (start_time, run_time) = get_start_and_run_time(*handle, now);
                 let mut p = Self {
@@ -439,8 +439,8 @@ impl ProcessInner {
                     cmd: Vec::new(),
                     environ: Vec::new(),
                     exe,
-                    cwd: PathBuf::new(),
-                    root: PathBuf::new(),
+                    cwd: None,
+                    root: None,
                     status: ProcessStatus::Run,
                     memory,
                     virtual_memory,
@@ -463,7 +463,7 @@ impl ProcessInner {
             let exe = if refresh_kind.exe().needs_update(|| true) {
                 get_executable_path(pid)
             } else {
-                PathBuf::new()
+                None
             };
             Self {
                 handle: None,
@@ -474,8 +474,8 @@ impl ProcessInner {
                 cmd: Vec::new(),
                 environ: Vec::new(),
                 exe,
-                cwd: PathBuf::new(),
-                root: PathBuf::new(),
+                cwd: None,
+                root: None,
                 status: ProcessStatus::Run,
                 memory,
                 virtual_memory,
@@ -511,10 +511,7 @@ impl ProcessInner {
             get_process_user_id(self, refresh_kind);
             get_process_params(self, refresh_kind);
         }
-        if refresh_kind
-            .exe()
-            .needs_update(|| self.exe.as_os_str().is_empty())
-        {
+        if refresh_kind.exe().needs_update(|| self.exe.is_none()) {
             unsafe {
                 self.exe = match self.handle.as_ref() {
                     Some(handle) => get_exe(handle),
@@ -553,8 +550,8 @@ impl ProcessInner {
         &self.cmd
     }
 
-    pub(crate) fn exe(&self) -> &Path {
-        self.exe.as_path()
+    pub(crate) fn exe(&self) -> Option<&Path> {
+        self.exe.as_deref()
     }
 
     pub(crate) fn pid(&self) -> Pid {
@@ -565,12 +562,12 @@ impl ProcessInner {
         &self.environ
     }
 
-    pub(crate) fn cwd(&self) -> &Path {
-        self.cwd.as_path()
+    pub(crate) fn cwd(&self) -> Option<&Path> {
+        self.cwd.as_deref()
     }
 
-    pub(crate) fn root(&self) -> &Path {
-        self.root.as_path()
+    pub(crate) fn root(&self) -> Option<&Path> {
+        self.root.as_deref()
     }
 
     pub(crate) fn memory(&self) -> u64 {
@@ -672,23 +669,20 @@ unsafe fn get_process_times(handle: HANDLE) -> u64 {
 }
 
 // On Windows, the root folder is always the current drive. So we get it from its `cwd`.
-fn update_root(refresh_kind: ProcessRefreshKind, cwd: &Path, root: &mut PathBuf) {
-    if !refresh_kind
-        .root()
-        .needs_update(|| root.as_os_str().is_empty())
-    {
+fn update_root(refresh_kind: ProcessRefreshKind, cwd: &Path, root: &mut Option<PathBuf>) {
+    if !refresh_kind.root().needs_update(|| root.is_none()) {
         return;
     }
-    if cwd.as_os_str().is_empty() || !cwd.has_root() {
-        *root = PathBuf::new();
-        return;
-    }
-    let mut ancestors = cwd.ancestors().peekable();
-    while let Some(path) = ancestors.next() {
-        if ancestors.peek().is_none() {
-            *root = path.into();
-            break;
+    if cwd.has_root() {
+        let mut ancestors = cwd.ancestors().peekable();
+        while let Some(path) = ancestors.next() {
+            if ancestors.peek().is_none() {
+                *root = Some(path.into());
+                return;
+            }
         }
+    } else {
+        *root = None;
     }
 }
 
@@ -868,12 +862,8 @@ unsafe fn get_process_params(process: &mut ProcessInner, refresh_kind: ProcessRe
         || refresh_kind
             .environ()
             .needs_update(|| process.environ.is_empty())
-        || refresh_kind
-            .cwd()
-            .needs_update(|| process.cwd.as_os_str().is_empty())
-        || refresh_kind
-            .root()
-            .needs_update(|| process.root.as_os_str().is_empty()))
+        || refresh_kind.cwd().needs_update(|| process.cwd.is_none())
+        || refresh_kind.root().needs_update(|| process.root.is_none()))
     {
         return;
     }
@@ -1003,15 +993,11 @@ fn get_cwd_and_root<T: RtlUserProcessParameters>(
     params: &T,
     handle: HANDLE,
     refresh_kind: ProcessRefreshKind,
-    cwd: &mut PathBuf,
-    root: &mut PathBuf,
+    cwd: &mut Option<PathBuf>,
+    root: &mut Option<PathBuf>,
 ) {
-    let cwd_needs_update = refresh_kind
-        .cwd()
-        .needs_update(|| cwd.as_os_str().is_empty());
-    let root_needs_update = refresh_kind
-        .root()
-        .needs_update(|| root.as_os_str().is_empty());
+    let cwd_needs_update = refresh_kind.cwd().needs_update(|| cwd.is_none());
+    let root_needs_update = refresh_kind.root().needs_update(|| root.is_none());
     if !cwd_needs_update && !root_needs_update {
         return;
     }
@@ -1021,12 +1007,12 @@ fn get_cwd_and_root<T: RtlUserProcessParameters>(
             // Should always be called after we refreshed `cwd`.
             update_root(refresh_kind, &tmp_cwd, root);
             if cwd_needs_update {
-                *cwd = tmp_cwd;
+                *cwd = Some(tmp_cwd);
             }
         },
         Err(_e) => {
             sysinfo_debug!("get_cwd_and_root failed to get data: {:?}", _e);
-            *cwd = PathBuf::new();
+            *cwd = None;
         }
     }
 }
@@ -1116,7 +1102,7 @@ fn get_proc_env<T: RtlUserProcessParameters>(
     }
 }
 
-pub(crate) fn get_executable_path(_pid: Pid) -> PathBuf {
+pub(crate) fn get_executable_path(_pid: Pid) -> Option<PathBuf> {
     /*let where_req = format!("ProcessId={}", pid);
 
     if let Some(ret) = run_wmi(&["process", "where", &where_req, "get", "ExecutablePath"]) {
@@ -1127,7 +1113,7 @@ pub(crate) fn get_executable_path(_pid: Pid) -> PathBuf {
             return line.to_owned();
         }
     }*/
-    PathBuf::new()
+    None
 }
 
 #[inline]


Test patch:
diff --git a/tests/process.rs b/tests/process.rs
index 9fd414e..76b516a 100644
--- a/tests/process.rs
+++ b/tests/process.rs
@@ -34,7 +34,7 @@ fn test_cwd() {
 
     if let Some(p) = p {
         assert_eq!(p.pid(), pid);
-        assert_eq!(p.cwd(), std::env::current_dir().unwrap());
+        assert_eq!(p.cwd().unwrap(), &std::env::current_dir().unwrap());
     } else {
         panic!("Process not found!");
     }
@@ -166,13 +166,14 @@ fn test_process_refresh() {
         .process(sysinfo::get_current_pid().expect("failed to get current pid"))
         .is_some());
 
-    assert!(s.processes().iter().all(|(_, p)| p.environ().is_empty()
-        && p.cwd().as_os_str().is_empty()
-        && p.cmd().is_empty()));
     assert!(s
         .processes()
         .iter()
-        .any(|(_, p)| !p.exe().as_os_str().is_empty() && !p.name().is_empty() && p.memory() != 0));
+        .all(|(_, p)| p.environ().is_empty() && p.cwd().is_none() && p.cmd().is_empty()));
+    assert!(s
+        .processes()
+        .iter()
+        .any(|(_, p)| !p.name().is_empty() && p.memory() != 0));
 }
 
 #[test]
@@ -628,7 +629,6 @@ fn test_process_creds() {
 // This test ensures that only the requested information is retrieved.
 #[test]
 fn test_process_specific_refresh() {
-    use std::path::Path;
     use sysinfo::{DiskUsage, ProcessRefreshKind};
 
     if !sysinfo::IS_SUPPORTED || cfg!(feature = "apple-sandbox") {
@@ -645,9 +645,9 @@ fn test_process_specific_refresh() {
         }
         assert_eq!(p.environ().len(), 0);
         assert_eq!(p.cmd().len(), 0);
-        assert_eq!(p.exe(), Path::new(""));
-        assert_eq!(p.cwd(), Path::new(""));
-        assert_eq!(p.root(), Path::new(""));
+        assert_eq!(p.exe(), None);
+        assert_eq!(p.cwd(), None);
+        assert_eq!(p.root(), None);
         assert_eq!(p.memory(), 0);
         assert_eq!(p.virtual_memory(), 0);
         // These two won't be checked, too much lazyness in testing them...
@@ -723,8 +723,8 @@ fn test_process_specific_refresh() {
         target_os = "ios",
         feature = "apple-sandbox",
     )) {
-        update_specific_and_check!(root, with_root, , Path::new(""));
+        update_specific_and_check!(root, with_root, , None);
     }
-    update_specific_and_check!(exe, with_exe, , Path::new(""));
-    update_specific_and_check!(cwd, with_cwd, , Path::new(""));
+    update_specific_and_check!(exe, with_exe, , None);
+    update_specific_and_check!(cwd, with_cwd, , None);
 }