1

Suppose I have I directory structure like the following:

MyDirectory
> File1
> File2
> File3
> UndeletableFile
> File4

Suppose also that I have already implemented a recursive algorithm for deleting the content of a folder and then the folder itself and/or used one of the excellent suggestions here

Now here's the catch to which I can't seem to find a solution after some searching on the web and on SO:
I want to delete the entire folder, but I want to do it only if it will succeed for every file and subfolder that is in there. The current implementation (of java.io.File.delete() to be precise) will delete a file immediately upon being called on it and then move on to the next file. If the deletion of a folder fails halfway through like in the example above there will be UndeletableFile and File5 as well as MyDirectory left on the system.

Is there a convenince method or a best practise algorithm for this case?
The goal is to let the deletable files live if the operation would fail halfway through. After all they shouldn't die if their home will survive, right?

What I have thought about so far:
Maybe it would be smart to just rename all files first instead of deleting them, like so

MyDirectory
> File1temp
> File2temp
> ...

After that if the process fails I could traverse the directory again and re-rename all files back to normal and if it succeeds I could delete them for good.

But this seams very inefficient to me, isn't there a better solution? It seems to me like this should be a common problem, please provide a link if I have overlooked something

Community
  • 1
  • 1
avalancha
  • 1,457
  • 1
  • 22
  • 41
  • Who else works with this files besides your application? Who produces them? Other process? Is it managed by you or its 3rd party software? – Mikhail Jan 24 '14 at 09:23
  • @Mikhail The user may directly influence the folder. They are produced by another application. There is no other process running at that time. Managed by me. **But I want to stress:** It's ok if it fails now and then, I only want to be able to fall back to "don't delete anything" if something happens – avalancha Jan 24 '14 at 09:33

3 Answers3

2

There is no completely safe way to do this.

Your best bet will be to scan through all the files, check that you are allowed to delete them. Only if the scan succeeds do you then go through and do the actual deletion.

The catch is if something becomes locked after your scan but before you delete it then the deletion will still fail on that file.

The only alternative would be to keep a copy of all the files (i.e. move them to the recycle bin, store them in a zip, etc) until all the deletions have succeeded and only then empty the bin/delete the zip - if not restore.

But even then something could happen to block the restore.

Really you will have edge cases here, what you need to do is identify what they are and decide what the desired behaviour is in each case.

Tim B
  • 40,716
  • 16
  • 83
  • 128
  • could you elaborate on how to best check if I'm "allowed to delete" please? – avalancha Jan 24 '14 at 09:15
  • This should help: http://docs.oracle.com/javase/7/docs/api/java/io/File.html#canWrite%28%29 – Tim B Jan 24 '14 at 09:20
  • I think that duplicating them/move them to a zip is even more work than the idea of renaming, but other than that a good answer, thanks – avalancha Jan 24 '14 at 09:38
  • I agree, moving to a zip is a lot of work so I wouldn't really recommend that approach unless absolutely needed. Renaming doesn't stop something locking the files. The only way to prevent that is to remove it completely from the filing systems control. – Tim B Jan 24 '14 at 09:55
  • Maybe you can refer to this solution [how-can-i-implement-recycle-bin-functionality](http://stackoverflow.com/questions/6072828/how-can-i-implement-recycle-bin-functionality) – mojiayi Jan 24 '14 at 10:03
1

You can use first a non-destructive method to check if any file can be deleted, e.g. on UNIX check that the file itself and its containing directory is writable. Then recursively calculate which directories can be deleted, and then in a second pass delete those files and directories that reside inside directories calculated to be deletable. The problem with this approach is that someone else may modify directory or file contents or permissions while the operation is going on, so you can still get wrong results.

Renaming is not a fool-proof solution either because if a program has a handle to a file or a directory, it can retain that handle while the object is renamed. So even if you rename a file or move it to a 'safe' location, someone else can change its permissions if there was an open handle to the file.

There isn't a fool-proof method available that would work on all platform under all conditions, because the underlying filesystem can be manipulated by other processes at the same time.

For a single user scenario, using the two-pass approach outlined in the first paragraph should work.

Antti Huima
  • 25,136
  • 3
  • 52
  • 71
1

A common practice for your case, when you have to work with multiple files exclusively is to have a separate semaphore file and lock it before starting any operations on files bunch. A very primitive example would look like:

private static RandomAccessFile raf;

public static void main(String[] args) throws IOException {
    lock();
    //do smth...
    release();
}

private static boolean lock() throws IOException {
    File f = new File("Semaphore.lck");
    raf = new RandomAccessFile(f, "rw");
    FileLock fl = raf.getChannel().tryLock();
    return fl != null;
}

private static void release() throws IOException {
    raf.close();
}

A similar approach is taken in Apache Camel integration framework, have look at it's File component. The only problem is that second process should follow same protocol too, otherwise it would not have any effect.

Mikhail
  • 4,175
  • 15
  • 31
  • That's a great idea! But do I understand correctly that I will still have to combine this with the answer of Tim? Because this will not check if the file is currently available for me to lock in the first place, right? – avalancha Jan 24 '14 at 09:42
  • If user do not access file manually and everything is done through a semaphore-based protocol, you do not have to rename file. Once semaphore is acquired, you can delete files safely. This is pretty like a synchronized block in java. – Mikhail Jan 24 '14 at 09:45
  • 1
    Yes, the semaphore file works well - but only if _everything_ accessing those files uses the semaphore file. Even one process/user ignoring the file can break it. – Tim B Jan 24 '14 at 09:54