changeset 27241:733431da9742

dir.m: Gracefully handle race conditions when file is deleted (bug #56618). * dir.m: Don't enter main body of code if stat() call fails and number of files (nf) is 0. Use new variable cnt to place entries in to pre-declared info array. If lstat call fails on file, skip further processing and "continue" with for loop. At end of for loop over all files, delete any unused elements from info array.
author Rik <rik@octave.org>
date Fri, 12 Jul 2019 09:11:58 -0700
parents 336c640c481b
children 61701d1317a1
files scripts/miscellaneous/dir.m
diffstat 1 files changed, 13 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/scripts/miscellaneous/dir.m	Fri Jul 12 10:41:47 2019 -0400
+++ b/scripts/miscellaneous/dir.m	Fri Jul 12 09:11:58 2019 -0700
@@ -111,7 +111,7 @@
     endif
   endif
 
-  if (numel (flst) > 0)
+  if (nf > 0)
 
     fs = regexptranslate ("escape", filesep ("all"));
     re = sprintf ('(^.+)[%s]([^%s.]*)([.][^%s]*)?$', fs, fs, fs);
@@ -119,11 +119,13 @@
     info(nf,1).name = "";  # pre-declare size of struct array
 
     ## Collect results.
-    for i = nf:-1:1
+    cnt = 0;
+    for i = 1:nf
       fn = flst{i};
       [st, err, msg] = lstat (fn);
       if (err < 0)
         warning ("dir: 'lstat (%s)' failed: %s", fn, msg);
+        continue;
       else
         ## If we are looking at a link that points to something,
         ## return info about the target of the link, otherwise, return
@@ -136,27 +138,28 @@
         endif
         tmpdir = regexprep (fn, re, '$1');
         fn = regexprep (fn, re, '$2$3');
-        info(i).name = fn;
+        info(++cnt).name = fn;
         if (! strcmp (last_dir, tmpdir))
           ## Caching mechanism to speed up function
           last_dir = tmpdir;
           last_absdir = canonicalize_file_name (last_dir);
         endif
-        info(i).folder = last_absdir;
+        info(cnt).folder = last_absdir;
         lt = localtime (st.mtime);
-        info(i).date = strftime ("%d-%b-%Y %T", lt);
-        info(i).bytes = st.size;
-        info(i).isdir = S_ISDIR (st.mode);
-        info(i).datenum = [lt.year + 1900, lt.mon + 1, lt.mday, ...
+        info(cnt).date = strftime ("%d-%b-%Y %T", lt);
+        info(cnt).bytes = st.size;
+        info(cnt).isdir = S_ISDIR (st.mode);
+        info(cnt).datenum = [lt.year + 1900, lt.mon + 1, lt.mday, ...
                              lt.hour, lt.min, lt.sec];
-        info(i).statinfo = st;
+        info(cnt).statinfo = st;
       endif
     endfor
+    info((cnt+1):end) = [];  # remove any unused entries
     ## A lot of gymnastics in order to call datenum just once.  2x speed up.
     dvec = [info.datenum]([[1:6:end]', [2:6:end]', [3:6:end]', ...
                            [4:6:end]', [5:6:end]', [6:6:end]']);
     dnum = datenum (dvec);
-    ctmp = mat2cell (dnum, ones (nf,1), 1);
+    ctmp = mat2cell (dnum, ones (cnt,1), 1);
     [info.datenum] = ctmp{:};
   endif