Coverage Report - org.webslinger.commons.vfs.cow.COWFsck
 
Classes in this File Line Coverage Branch Coverage Complexity
COWFsck
81%
125/155
98%
43/44
0
COWFsck$1
11%
1/9
N/A
0
COWFsck$MessageHandler
N/A
N/A
0
COWFsck$Selector
100%
3/3
N/A
0
 
 1  
 package org.webslinger.commons.vfs.cow;
 2  
 
 3  
 import java.io.File;
 4  
 import java.io.IOException;
 5  
 import java.util.HashSet;
 6  
 
 7  
 import org.apache.commons.vfs.FileObject;
 8  
 import org.apache.commons.vfs.FileSelectInfo;
 9  
 import org.apache.commons.vfs.FileSelector;
 10  
 import org.apache.commons.vfs.FileSystem;
 11  
 import org.apache.commons.vfs.FileSystemException;
 12  
 import org.apache.commons.vfs.FileType;
 13  
 import org.apache.commons.vfs.Selectors;
 14  
 import org.apache.commons.vfs.impl.StandardFileSystemManager;
 15  
 
 16  
 import org.webslinger.io.IOUtil;
 17  
 
 18  
 public class COWFsck {
 19  
     private final FileObject base;
 20  
     private final MessageHandler messageHandler;
 21  
 
 22  1
     public COWFsck(FileObject base, MessageHandler messageHandler) throws FileSystemException {
 23  1
         this.base = base;
 24  1
         this.messageHandler = messageHandler;
 25  1
     }
 26  
 
 27  
     public static void main(String[] args) throws Exception {
 28  0
         StandardFileSystemManager sfsm = new StandardFileSystemManager();
 29  0
         sfsm.init();
 30  0
         for (String arg: args) {
 31  0
             COWFsck fsck = new COWFsck(sfsm.createVirtualFileSystem(sfsm.toFileObject(new File(arg))));
 32  0
             fsck.run();
 33  
         }
 34  0
     }
 35  
 
 36  
     public COWFsck(FileObject base) throws FileSystemException {
 37  0
         this(base, systemMessageHandler);
 38  0
     }
 39  
 
 40  
     public void run() throws IOException {
 41  1
         base.findFiles(new Selector());
 42  1
     }
 43  
 
 44  
     protected void processFolder(FileObject base, FileObject folder) throws IOException {
 45  17
         FileObject cowStateFile = folder.resolveFile(".cowstate.xml");
 46  17
         FileObject cowFile = folder.resolveFile(".cow");
 47  17
         boolean hadError = false;
 48  17
         if (verifyCOWFile(cowFile)) hadError = true;
 49  17
         if (verifyCOWStateFile(cowStateFile)) hadError = true;
 50  17
         if (cowFile.exists()) {
 51  12
             if (cowStateFile.exists()) {
 52  2
                 if (hadError) {
 53  0
                     messageHandler.warn(folder, 9, "Both .cow and .cowstate exists, not removing due to previous errors");
 54  
                 } else {
 55  2
                     messageHandler.warn(folder, 10, "Both .cow and .cowstate exists, removing .cow");
 56  2
                     cowFile.delete(Selectors.SELECT_SELF_AND_CHILDREN);
 57  
                 }
 58  
             } else {
 59  10
                 if (hadError) {
 60  1
                     messageHandler.info(folder, 11, "Not converting .cow to .cowstate.xml due to previous errors");
 61  
                 } else {
 62  9
                     convertCOWDirToCOWState(cowFile, cowStateFile);
 63  
                 }
 64  
             }
 65  
         }
 66  17
         detectUndeletes(folder, cowStateFile);
 67  17
     }
 68  
 
 69  
     protected void detectUndeletes(FileObject folder, FileObject cowStateFile) throws IOException {
 70  17
         if (!cowStateFile.exists()) return;
 71  
         try {
 72  7
             COWState cowState = new COWState(cowStateFile);
 73  11
             for (String name: cowState.getDeletedEntries()) {
 74  4
                 if (folder.resolveFile(name).exists()) {
 75  1
                     cowState.getEntry(name, true).setDeleted(false);
 76  
                 }
 77  
             }
 78  2
         } catch (IOException e) {
 79  2
             messageHandler.error(cowStateFile, 8, "couldn't load .cowstate.xml file", e);
 80  5
         }
 81  7
     }
 82  
 
 83  
     protected boolean verifyCOWStateFile(FileObject cowStateFile) throws IOException {
 84  17
         if (!cowStateFile.exists()) return false;
 85  
         try {
 86  4
             COWState cowState = new COWState(cowStateFile);
 87  0
         } catch (IOException e) {
 88  0
             messageHandler.error(cowStateFile, 8, "couldn't load .cowstate.xml file", e);
 89  0
             return true;
 90  4
         }
 91  4
         return false;
 92  
     }
 93  
 
 94  
     protected boolean scanCOWSubDir(FileObject subdir) throws FileSystemException {
 95  
         //System.err.println("scanCOWSubDir(" + subdir + ")");
 96  36
         if (!subdir.exists()) return false;
 97  21
         if (!subdir.getType().hasChildren()) {
 98  3
             messageHandler.warn(subdir, 3, "is not a folder, removing");
 99  3
             subdir.delete();
 100  3
             return false;
 101  
         }
 102  18
         boolean hadError = false;
 103  36
         for (FileObject child: subdir.getChildren()) {
 104  18
             String baseName = child.getName().getBaseName();
 105  18
             if (child.getType().hasChildren()) {
 106  3
                 messageHandler.warn(child, 4, "is a folder, removing");
 107  3
                 child.delete(Selectors.SELECT_SELF_AND_CHILDREN);
 108  15
             } else if (baseName.equals(".cow")) {
 109  3
                 messageHandler.warn(child, 5, "is a nested .cow, removing");
 110  3
                 child.delete(Selectors.SELECT_SELF_AND_CHILDREN);
 111  3
                 continue;
 112  12
             } else if (baseName.equals(".cowstate.xml")) {
 113  3
                 messageHandler.warn(child, 6, "is a nested .cowstate.xml, removing");
 114  3
                 child.delete(Selectors.SELECT_SELF_AND_CHILDREN);
 115  
             } else {
 116  
                 try {
 117  9
                     IOUtil.readString(child.getContent().getInputStream(), "UTF-8");
 118  1
                 } catch (IOException e) {
 119  1
                     messageHandler.error(child, 7, "could not read contents of file, possible encoding problem", e);
 120  1
                     hadError = true;
 121  8
                 }
 122  
             }
 123  
         }
 124  18
         return hadError;
 125  
     }
 126  
 
 127  
     protected boolean verifyCOWFile(FileObject cowFile) throws FileSystemException {
 128  17
         if (!cowFile.exists()) return false;
 129  13
         FileObject deletedFile = cowFile.resolveFile("deleted");
 130  13
         FileObject linkFile = cowFile.resolveFile("link");
 131  13
         FileObject cowLinksFile = cowFile.resolveFile("cow-links");
 132  13
         boolean hadError = false;
 133  13
         if (!cowFile.getType().hasChildren()) {
 134  1
             messageHandler.warn(cowFile, 1, "has no children, removing");
 135  1
             cowFile.delete(Selectors.SELECT_SELF_AND_CHILDREN);
 136  1
             return false;
 137  
         }
 138  12
         if (scanCOWSubDir(deletedFile)) hadError = true;
 139  12
         if (scanCOWSubDir(linkFile)) hadError = true;
 140  12
         if (scanCOWSubDir(cowLinksFile)) hadError = true;
 141  12
         HashSet<String> deletedSet = new HashSet<String>();
 142  17
         for (FileObject child: getChildren(deletedFile)) {
 143  5
             deletedSet.add(child.getName().getBaseName());
 144  
         }
 145  15
         for (FileObject child: getChildren(linkFile)) {
 146  3
             String name = child.getName().getBaseName();
 147  3
             if (deletedSet.contains(name)) {
 148  1
                 messageHandler.warn(child, 2, "has been deleted, removing");
 149  1
                 child.delete();
 150  
             }
 151  
         }
 152  
         // check for symlink and deleted at the same time
 153  12
         return hadError;
 154  
     }
 155  
 
 156  1
     private static final FileObject[] EMPTY_ARRAY = new FileObject[0];
 157  
     protected FileObject[] getChildren(FileObject folder) throws FileSystemException {
 158  36
         if (!folder.exists()) return EMPTY_ARRAY;
 159  19
         return folder.getChildren();
 160  
     }
 161  
 
 162  
     protected void convertCOWDirToCOWState(FileObject cowFile, FileObject cowStateFile) throws IOException {
 163  9
         FileObject deletedFile = cowFile.resolveFile("deleted");
 164  9
         FileObject linkFile = cowFile.resolveFile("link");
 165  9
         FileObject cowLinksFile = cowFile.resolveFile("cow-links");
 166  9
         deletedFile.delete();
 167  9
         linkFile.delete();
 168  9
         cowLinksFile.delete();
 169  9
         boolean subDeletionFailed = false;
 170  9
         if (cowFile.getChildren().length != 0) {
 171  4
             messageHandler.info(cowFile.getParent(), 12, "Converting .cow to .cowstate.xml");
 172  4
             COWState cowState = new COWState(cowStateFile);
 173  4
             boolean childDeletionFailed = false;
 174  5
             for (FileObject child: getChildren(linkFile)) {
 175  1
                 String target = IOUtil.readString(child.getContent().getInputStream(), "UTF-8");
 176  1
                 cowState.getEntry(child.getName().getBaseName(), true).setSymlink(target);
 177  1
                 if (!child.delete()) {
 178  0
                     childDeletionFailed = true;
 179  0
                     messageHandler.warn(child, 13, "couldn't delete");
 180  
                 }
 181  1
                 child.delete();
 182  
             }
 183  4
             if (childDeletionFailed) {
 184  0
                 subDeletionFailed = true;
 185  0
                 childDeletionFailed = false;
 186  
             } else {
 187  4
                 if (linkFile.exists() && !linkFile.delete()) {
 188  0
                     subDeletionFailed = true;
 189  0
                     messageHandler.warn(linkFile, 13, "couldn't delete");
 190  
                 }
 191  
             }
 192  5
             for (FileObject child: getChildren(cowLinksFile)) {
 193  1
                 String target = IOUtil.readString(child.getContent().getInputStream(), "UTF-8");
 194  1
                 cowState.getEntry(child.getName().getBaseName(), true).addBase(target);
 195  1
                 if (!child.delete()) {
 196  0
                     childDeletionFailed = true;
 197  0
                     messageHandler.warn(child, 13, "couldn't delete");
 198  
                 }
 199  1
                 child.delete();
 200  
             }
 201  4
             if (childDeletionFailed) {
 202  0
                 subDeletionFailed = true;
 203  0
                 childDeletionFailed = false;
 204  
             } else {
 205  4
                 if (cowLinksFile.exists() && !cowLinksFile.delete()) {
 206  0
                     subDeletionFailed = true;
 207  0
                     messageHandler.warn(cowLinksFile, 13, "couldn't delete");
 208  
                 }
 209  
             }
 210  7
             for (FileObject child: getChildren(deletedFile)) {
 211  3
                 cowState.getEntry(child.getName().getBaseName(), true).setDeleted(true);
 212  3
                 if (!child.delete()) {
 213  0
                     childDeletionFailed = true;
 214  0
                     messageHandler.warn(child, 13, "couldn't delete");
 215  
                 }
 216  
             }
 217  4
             if (childDeletionFailed) {
 218  0
                 subDeletionFailed = true;
 219  0
                 childDeletionFailed = false;
 220  
             } else {
 221  4
                 if (deletedFile.exists() && !deletedFile.delete()) {
 222  0
                     subDeletionFailed = true;
 223  0
                     messageHandler.warn(deletedFile, 13, "couldn't delete");
 224  
                 }
 225  
             }
 226  
         }
 227  9
         if (!subDeletionFailed) {
 228  9
             if (!cowFile.delete()) {
 229  1
                 messageHandler.warn(cowFile, 14, "couldn't delete .cow");
 230  
             }
 231  
         }
 232  
 
 233  9
     }
 234  
 
 235  
     protected boolean traverseDescendents(FileSelectInfo info) {
 236  29
         FileObject file = info.getFile();
 237  29
         if (file.getName().getBaseName().equals(".cow")) return false;
 238  17
         return true;
 239  
     }
 240  
 
 241  
     protected boolean includeFile(FileSelectInfo info) throws IOException {
 242  35
         FileObject file = info.getFile();
 243  35
         if (file.getName().getBaseName().equals(".cow")) return false;
 244  22
         if (file.getType().hasChildren()) {
 245  17
             processFolder(info.getBaseFolder(), file);
 246  
         }
 247  22
         return false;
 248  
     }
 249  
 
 250  1
     protected class Selector implements FileSelector {
 251  
         public boolean traverseDescendents(FileSelectInfo info) {
 252  
 //            System.err.println("traverseDescendents(" + info.getFile() + ")");
 253  29
             return COWFsck.this.traverseDescendents(info);
 254  
         }
 255  
 
 256  
         public boolean includeFile(FileSelectInfo info) throws IOException {
 257  
 //            System.err.println("includeFile(" + info.getFile() + ")");
 258  35
             return COWFsck.this.includeFile(info);
 259  
         }
 260  
     }
 261  
 
 262  
     public interface MessageHandler {
 263  
         void warn(FileObject file, int code, String message);
 264  
         void info(FileObject file, int code, String message);
 265  
         void error(FileObject file, int code, String message);
 266  
         void error(FileObject file, int code, String message, Exception e);
 267  
     }
 268  
 
 269  1
     public static final MessageHandler systemMessageHandler = new MessageHandler() {
 270  
         public void warn(FileObject file, int code, String message) {
 271  0
             System.err.println("warning(" + code + ':' + file.getName().getPath() + "): " + message);
 272  0
         }
 273  
 
 274  
         public void info(FileObject file, int code, String message) {
 275  0
             System.out.println("info(" + code + ':' + file.getName().getPath() + "): " + message);
 276  0
         }
 277  
 
 278  
         public void error(FileObject file, int code, String message) {
 279  0
             System.err.println("error(" + code + ':' + file.getName().getPath() + "): " + message);
 280  0
         }
 281  
 
 282  
         public void error(FileObject file, int code, String message, Exception e) {
 283  0
             System.err.println("error(" + code + ':' + file.getName().getPath() + "): " + message);
 284  
             //e.printStackTrace(System.err);
 285  0
         }
 286  
     };
 287  
 }