Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java how to delete a file that has the IMMUTABLE bit set

Tags:

java

Working on a Java 8 project where I copy files from external sources. In one of these sources a file is set with the immutable bit flag.

In OSX this is set like so

sudo chflags schg /path/to/file

In Linux

chattr +i /path/to/file

I now need to delete the files that I have copied. I had been using Apache Commons IO to delete the directory like so,

FileUtils.deleteDirectory(new File("/path/here"));

However this crashes out with a java.io.IOException exception.

Is there any Cross Platform way to delete these files? The process running is the owner of the file.

like image 986
Ben Boyter Avatar asked Nov 30 '16 07:11

Ben Boyter


People also ask

How do you force delete a file in Java?

To force delete file using Java, we can use the FileUtils or FileDeleteStrategy class available in Apache Commons Io. We can also use FileDeleteStrategy class of apache commons io to force delete file, even if the file represents a non-enpty directory . the delete() method deletes the file object.

How do you delete a file if already exists in Java?

files. deleteifexists(Path p) method defined in Files package: This method deletes a file if it exists. It also deletes a directory mentioned in the path only if the directory is not empty. Returns: It returns true if the file was deleted by this method; false if it could not be deleted because it did not exist.

How do you delete a file in Java?

In Java, we can delete a file by using the File. delete() method of File class. The delete() method deletes the file or directory denoted by the abstract pathname. If the pathname is a directory, that directory must be empty to delete.

How do I delete a file using Java NIO?

In Java, we can use the NIO Files. delete(Path) and Files. deleteIfExists(Path) to delete a file.


2 Answers

Problem : Since, we know that the file with immutable attribute set, cannot be deleted by any user. Even The root user cannot delete the following file.

Now, to be able to delete the file you have to remove the immutable attribute and then delete the file.

Now, what you have to do is to apply the shell command through the code and remove the immutable attribute on the file.

As you have mentioned the cross platform issue

So the basic algo would be


Step 1: Detect which OS you are using
sample code would be:
private static String OS = System.getProperty("os.name").toLowerCase();
if(OS.indexOf("win") >= 0)
    //your code for windows OS.
else if(OS.indexOf("mac") >= 0)
    //your code for MAC OS.
else if(OS.indexOf("sunos") >= 0)
    //your code for Solaris OS

Note: I haven't added code for checking all OS's. So have a look at them by yourself.
Step 2: This step helps you solve the Cross platform issue
Issue the appropriate shell command through java.lang.Runtime.exec to remove the immutable attribute of the file.
Look at the java.lang.Runtime.exec.
java.lang.Runtime.exec : Through this, you supply the appropriate shell command for any underlying Environment, whether be MAC, Windows, Linux etc.

sample code would be

//if the OS detected is Linux then
Process p = Runtime.getRuntime().exec("chattr -i /path/to/file");
//Play with the process as you would like to, using the documentation.
//else if the OS detected is OSX then : example to unlock in OSX
Process p = Runtime.getRuntime().exec("chflags nouchg /path/to/file");
//Play with the process as you would like to, using the documentation.
//else if the OS detected is say Windows then : example to unlock in    Windows
Process p = Runtime.getRuntime().exec("ATTRIB -s -h /path/to/file");
//-s -h are used to unlock and unhide (i dont the Antonym of hide :p ) the file in windows
//Play with the process as you would like to, using the documentation.

Note: To run other shell commands which include pipes you can use this kind of sample code:

Process p = Runtime.getRuntime().exec(new String[]{"csh","-c","cat /home/narek/pk.txt"});


Step 3: Use the same Runtime.exec() to check whether the immutable attribute has been removed or not, like this:

Process p = Runtime.getRuntime().exec("lsattr /whateverPath");
//Play with the process to check whether the attribute is set or not, using the documentation.


Step 4: If Step3 is true|false, whatever the logic you apply, then Delete the file using your respective command again using the Runtime.exec().

Thing is to have a look at the above linked documentation of java.lang.Runtime.exec and work with it.
please, let me know whether it helped to some extent or not.

like image 80
Tahir Hussain Mir Avatar answered Sep 23 '22 12:09

Tahir Hussain Mir


Is there any Cross Platform way to delete these files?

Not that I was able to find. I had to adopt the obvious solution you already considered, the long way round:

  • detect on which OS I'm running,
  • issue the appropriate shell command to remove the immutable bit
  • Verify that it has been removed.
  • and finally delete (in my case, change) the file.

Actually a simpler, and lazier, shortcut would be:

  • execute the unlocking command blindly for all the known binaries (/usr/bin/chattr, etc.), with full path to avoid possible security problems. They will all quickly fail except one; on e.g. Linux any call to ATTRIB.EXE or chflags will fail. If they all fail, the system is not supported.
  • execute with the appropriate parameters the one binary that did not fail, or the corresponding check binary file (e.g., lsattr for chattr), to verify flag removal.
  • or you can just try deleting the file, and catch java.io.IOException.

I started looking into what it was that chattr did, but it was immediately clear that there were several specialized, OS-specific system calls I would have needed to issue: so there was no real advantage over calling an external binary, except perhaps performance, but that wasn't an issue in my case.

Once one goes on the OS-specific (native) road, through either JNI or JNA, on a Linux system it is a "simple" matter of invoking the appropriate IOCTL function.

Note that it is not enough to identify the OS, you also need to identify the filesystem, because on e.g. FAT32 filesystems or NTFS-3G FSs, the IOCTL will return an error (the flag is either not available at low level, or not (yet) supported by the IOSS intermediate driver).

The same problem exists in Python 2.7, where at high level a OS independent call is supplied... but it does not work everywhere.

like image 34
LSerni Avatar answered Sep 24 '22 12:09

LSerni