www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - errors with filesystem operations

reply Hugo Florentino <hugo acdam.cu> writes:
Hi,

I am trying to make a little application to revert the effect of some 
viruses on USB memories, and running it from Windows encountered some 
exceptions that IMHO should not have happened.
Maybe I am missing something. Can you please check the commented blocks 
for errors?

Also, is there a way from Linux to remove attributes readonly, system, 
hidden ?

Regards, Hugo



import std.stdio;
import std.path;
import std.regex;
import std.file;
import std.string;

static string info = "CureUSB 0.3 (2014-01-14)";
static string str_moved   = "Moved";
static string str_deleted = "Deleted";
static string str_finished   = "Done";
static string str_setattrib = "Attributes set";
static string str_safename = "SafeToDelete";

// Expression to match directories with blanks for name
static auto re = regex(`^\s+$`);

version(Windows)
{
   import core.sys.windows.windows;
   extern(Windows) uint SetFileAttributesA(LPCSTR, DWORD);
}

int main()
{
   string ExePath = thisExePath();
   string SearchPath = rootName(ExePath);
   string BlankDirToDelete;

   version(Windows) SetConsoleOutputCP(65001);
   writefln(info);

   foreach(DirEntry d; dirEntries(SearchPath, SpanMode.shallow))
   {
     auto m = matchFirst(baseName(d.name), re);
     if (m.captures.length > 0)
     {
       if (isDir(d.name))
       {
         foreach(DirEntry f; dirEntries(d.name, SpanMode.shallow))
         {
           string origname = buildPath(SearchPath, baseName(f.name));
           rename(f.name, origname);
           writefln(`%s "%s"`, str_moved, origname);
         }
         BlankDirToDelete = d.name;
       }

       break;
     }
   }

   // The following block does not work; supposedly the process does not 
have
   // access to the directory because it is being used by another 
process,
   // which does not seem to be the case. Uncomment to try
   /*
   if (exists(BlankDirToDelete)) try
   {
     string SafeToBeDeleted = buildPath(SearchPath, str_safename)
     rename(BlankDirToDelete, SafeToBeDeleted);
     remove(SafeToBeDeleted);
   }
   catch (FileException e)
     writeln(e.msg);
   */

   foreach(DirEntry d; dirEntries(SearchPath, SpanMode.shallow))
   {
     if (d.name != ExePath)
     {
       string bname = baseName(d.name);
       version(Windows)
       {
         if ( (bname != "RECYCLER")
           && (bname != "$Recycle.bin")
           && (bname != "System Volume Information")
           && (bname != "Recovery")
           && (bname != "MSOCache")
         ) SetFileAttributesA(toStringz(d.name), FILE_ATTRIBUTE_NORMAL + 
FILE_ATTRIBUTE_ARCHIVE);
       }

       // The following block gives a FileException, claiming that the 
specified file
       // could not befound. Uncomment to try
       /*
       string exten = extension(d.name);
       if (exten.length > 0)
       {
         // writefln(`"%s" "%s"`, d.name, extension(d.name)); //debug 
line
         if ( (exten == ".exe")
           || (exten == ".lnk")
           || (exten == ".scr")
           || (exten == ".cpl")
           || (exten == ".hta")
           || (exten == ".com")
           || (exten == ".bat")
           || (exten == ".vb")
           || (exten == ".vbs") )
           if (isDir(buildPath(SearchPath, 
stripExtension(baseName(d.name)))))
           {
             remove(d.name);
             writefln(`%s "%s"`, str_deleted, d.name);
           }
       }
       */
     }
   }

   writefln("%s", str_setattrib);

   writefln("%s.", str_finished);

   return 0;
}
Jan 15 2014
next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2014-01-15 11:33, Hugo Florentino wrote:
 Hi,

 I am trying to make a little application to revert the effect of some
 viruses on USB memories, and running it from Windows encountered some
 exceptions that IMHO should not have happened.
 Maybe I am missing something. Can you please check the commented blocks
 for errors?

 Also, is there a way from Linux to remove attributes readonly, system,
 hidden ?
As of 2.065 (not release yet) you can set file attributes using std.file.setAttributes [1]. On Linux, hidden files are all files where the filename starts with a dot. To make it visible you need to rename the file. As far as I know, Linux doesn't have any form of system attribute. [1] https://github.com/D-Programming-Language/phobos/commit/5ab8dae665c27ed45eced244720e23e53ef23457 -- /Jacob Carlborg
Jan 15 2014
parent Hugo Florentino <hugo acdam.cu> writes:
On Wed, 15 Jan 2014 13:41:50 +0100, Jacob Carlborg wrote:
 ... On Linux, hidden files are all files where
 the filename starts with a dot. To make it visible you need to rename
 the file. As far as I know, Linux doesn't have any form of system
 attribute.
I am aware of this. However, FAT32 and NTFS (through ntfs-3g) and even ExFAT (I do not remember now the name of the project) are supported by Linux, so there should be a way to alter file structure so as to change attributes even if the OS does not natively provide a function for this (why should a user working in Linux be forced to reboot in Windows not even to run an unported application but just to change file attributes in a filesystem the OS supports)? Has anyone attempted it from D?
Jan 15 2014
prev sibling parent reply "Kagamin" <spam here.lot> writes:
Probably because you use ansi api: if filename contains 
non-english character, there could be a problem.

A filesystem support is primarily for storing files, attributes 
can be safely ignored.
Jan 16 2014
parent reply Hugo Florentino <hugo acdam.cu> writes:
On Thu, 16 Jan 2014 15:41:06 +0000, Kagamin wrote:
 Probably because you use ansi api: if filename contains non-english
 character, there could be a problem.

 A filesystem support is primarily for storing files, attributes can
 be safely ignored.
Hmm... that may be true for Linux filesystems, but not necessarily for filesystems used by Windows, where attributes do matter. Probably the majority of people here works from Linux and maybe most have the good fortune that the rest of the users in their organization either works in Linux too, or are computer literate persons. However where I work all users work in Windows (there is a specific application for the management of music rights which has not been ported to Linux AFAIK) and most of them have rather basic computer skills. So these users sometimes bring a USB memory from home, infected with some virus which the company antivirus does disinfect, but the effects (hidden folders or files moved to a directory with a blank name, among others) are not always reversed. So you see, I would prefer distributing an executable which fixes the memory for them rather than having them come into my office and make me reboot to Windows just to remove the hidden attribute from their folders or so on. Besides, if in my application I specifically set console codepage to UTF8 for the Windows version of the executable, and I am calling a D function (supposedly portable) to rename or delete a directory, and yet it does not get deleted, supposedly because it is being used (which I have tried to avoid in my code), I find this rather odd, and since I am new to D, I do not yet know the internals of the language well enough to realize where the problem is. Which is why I am asking for help here :)
Jan 16 2014
parent reply "Kagamin" <spam here.lot> writes:
Does it fail for that one directory only or for any directory?
Jan 16 2014
parent reply Hugo Florentino <hugo acdam.cu> writes:
On Fri, 17 Jan 2014 07:07:35 +0000, Kagamin wrote:
 Does it fail for that one directory only or for any directory?
Interesting question. I would have to do more tests with odd names, but apparently both remove() and rename() have problems with directories with a blank name. Curiously, moving files or directories from within that blank directory to another directory seems to working correctly, so it's not like the directory is not being detected. I haven't dived in the code for both functions, but I suspect that some validation may be causing the problem.
Jan 17 2014
next sibling parent reply "Kagamin" <spam here.lot> writes:
I only noticed that rename uses MOVEFILE_REPLACE_EXISTING flag, 
which can't be used with directories. Are you sure remove fails 
too? If rename throws, remove is not called in your code.
Jan 17 2014
parent reply Hugo Florentino <hugo acdam.cu> writes:
On Fri, 17 Jan 2014 13:10:00 +0000, Kagamin wrote:
 I only noticed that rename uses MOVEFILE_REPLACE_EXISTING flag, which
 can't be used with directories. Are you sure remove fails too? If
 rename throws, remove is not called in your code.
Well, as a matter of fact I did try to use remove directly and it failed with the same problem (directory in use by some process) Since subdirectories were being moved just fine, I thought once the content of the problematic directory was moved elsewhere I could rename the directory first and then remove it, but this approach failed too, at least from Windows.
Jan 17 2014
parent reply "Kagamin" <spam here.lot> writes:
remove uses DeleteFile, but MSDN says
To remove an empty directory, use the RemoveDirectory function.

so remove probably won't work on directories.
Jan 18 2014
next sibling parent Hugo Florentino <hugo acdam.cu> writes:
On Sat, 18 Jan 2014 11:51:48 +0000, Kagamin wrote:
 remove uses DeleteFile, but MSDN says
 To remove an empty directory, use the RemoveDirectory function.

 so remove probably won't work on directories.
You are correct, so I made a few test using rmdir() with a blank for directory name and now I get: d:\ : Directory is not empty. However, the diretory is certainly empty. Go figure! Note that in Windows I cannot create a directoy with a blank name from D either, so in order to test this, I have to create it elsewhere.
Jan 20 2014
prev sibling parent Hugo Florentino <hugo acdam.cu> writes:
Update: the combination of both your suggestions worked:

   if (exists(BlankDirToDelete))
   {
     try
       rmdir(`\\?\` ~ BlankDirToDelete);
     catch (FileException e)
       writeln(e.msg);
   }

Thanks! Now I just have to find out why the block of the file 
extensions is failing.
Jan 20 2014
prev sibling parent reply "Kagamin" <spam here.lot> writes:
On Friday, 17 January 2014 at 12:52:09 UTC, Hugo Florentino wrote:
 On Fri, 17 Jan 2014 07:07:35 +0000, Kagamin wrote:
 Does it fail for that one directory only or for any directory?
Interesting question. I would have to do more tests with odd names, but apparently both remove() and rename() have problems with directories with a blank name.
In that case try a path with \\?\ prefix (unparsed path).
Jan 18 2014
parent Hugo Florentino <hugo acdam.cu> writes:
On Sat, 18 Jan 2014 11:33:16 +0000, Kagamin wrote:
 On Friday, 17 January 2014 at 12:52:09 UTC, Hugo Florentino wrote:
 On Fri, 17 Jan 2014 07:07:35 +0000, Kagamin wrote:
 Does it fail for that one directory only or for any directory?
Interesting question. I would have to do more tests with odd names, but apparently both remove() and rename() have problems with directories with a blank name.
In that case try a path with \\?\ prefix (unparsed path).
Does not work. I modified the relevant block like this: if (exists(BlankDirToDelete)) { string SafeToBeDeleted = buildPath(`\\?\`, SearchPath, baseName(BlankDirToDelete)); writefln(`"%s"`, SafeToBeDeleted); try remove(SafeToBeDeleted); catch (FileException e) writeln(e.msg); try remove(`\\?\` ~ SafeToBeDeleted); catch (FileException e) writeln(e.msg); } The result? "d:\ " d:\ : Access denied. \\?\d:\ : Access denied. This happens even while executing the prompt as administrator, so it's not a problem of file privileges. Note that from the Windows explorer I am also having problems renaming or deleting the problematic directory, however with something else like Total Commander, I can both rename and delete with no issues. Regards, Hugo
Jan 20 2014