001package co.codewizards.cloudstore.updater; 002 003import static co.codewizards.cloudstore.core.util.Util.*; 004 005import java.io.BufferedInputStream; 006import java.io.BufferedOutputStream; 007import java.io.File; 008import java.io.FileFilter; 009import java.io.FileInputStream; 010import java.io.FileOutputStream; 011import java.io.IOException; 012import java.io.InputStream; 013import java.io.OutputStream; 014import java.util.zip.Deflater; 015 016import org.apache.commons.compress.archivers.tar.TarArchiveEntry; 017import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; 018import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; 019import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream; 020import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream; 021import org.apache.commons.compress.compressors.gzip.GzipParameters; 022import org.slf4j.Logger; 023import org.slf4j.LoggerFactory; 024 025public class TarGzFile { 026 private static final Logger logger = LoggerFactory.getLogger(TarGzFile.class); 027 028 private final File tarGzFile; 029 private TarGzEntryNameConverter tarGzEntryNameConverter; 030 private FileFilter fileFilter; 031 032 public TarGzFile(final File tarGzFile) { 033 this.tarGzFile = assertNotNull("tarGzFile", tarGzFile); 034 } 035 036 /** 037 * Gets the {@link FileFilter} deciding whether to process a file or not. 038 * @return the {@link FileFilter} deciding whether to process a file or not. May be <code>null</code>. 039 * If there is none, all files are processed. 040 */ 041 public FileFilter getFileFilter() { 042 return fileFilter; 043 } 044 public void setFileFilter(FileFilter fileFilter) { 045 this.fileFilter = fileFilter; 046 } 047 public TarGzFile fileFilter(FileFilter fileFilter) { 048 setFileFilter(fileFilter); 049 return this; 050 } 051 052 public TarGzEntryNameConverter getTarGzEntryNameConverter() { 053 return tarGzEntryNameConverter; 054 } 055 public void setTarGzEntryNameConverter(TarGzEntryNameConverter tarGzEntryNameConverter) { 056 this.tarGzEntryNameConverter = tarGzEntryNameConverter; 057 } 058 public TarGzFile tarGzEntryNameConverter(TarGzEntryNameConverter tarGzEntryNameConverter) { 059 setTarGzEntryNameConverter(tarGzEntryNameConverter); 060 return this; 061 } 062 063 public void compress(final File rootDir) throws IOException { 064 boolean deleteIncompleteTarGzFile = false; 065 final FileOutputStream fout = new FileOutputStream(tarGzFile); 066 try { 067 deleteIncompleteTarGzFile = true; 068 069 final GzipParameters gzipParameters = new GzipParameters(); 070 gzipParameters.setCompressionLevel(Deflater.BEST_COMPRESSION); 071 final TarArchiveOutputStream out = new TarArchiveOutputStream(new GzipCompressorOutputStream(new BufferedOutputStream(fout), gzipParameters)); 072 try { 073 writeTar(out, rootDir, rootDir); 074 } finally { 075 out.close(); 076 } 077 deleteIncompleteTarGzFile = false; 078 } finally { 079 fout.close(); 080 if (deleteIncompleteTarGzFile) 081 tarGzFile.delete(); 082 } 083 } 084 085 private static final TarGzEntryNameConverter defaultEntryNameConverter = new DefaultTarGzEntryNameConverter(); 086 087 private void writeTar(final TarArchiveOutputStream out, final File rootDir, final File dir) throws IOException { 088 final TarGzEntryNameConverter tarGzEntryNameConverter = this.tarGzEntryNameConverter == null ? defaultEntryNameConverter : this.tarGzEntryNameConverter; 089 try { 090 final File[] children = dir.listFiles(fileFilter); 091 if (children != null) { 092 for (final File child : children) { 093 final String entryName = tarGzEntryNameConverter.getEntryName(rootDir, child); 094 TarArchiveEntry archiveEntry = (TarArchiveEntry) out.createArchiveEntry(child, entryName); 095 096 if (child.canExecute()) 097 archiveEntry.setMode(archiveEntry.getMode() | 0111); 098 099 out.putArchiveEntry(archiveEntry); 100 try { 101 if (child.isFile()) { 102 final InputStream in = new FileInputStream(child); 103 try { 104 transferStreamData(in, out); 105 } finally { 106 in.close(); 107 } 108 } 109 } finally { 110 out.closeArchiveEntry(); 111 } 112 113 if (child.isDirectory()) 114 writeTar(out, rootDir, child); 115 } 116 } 117 } catch (IOException | RuntimeException x) { 118 logger.error(x.toString(), x); 119 throw x; 120 } 121 } 122 123 public void extract(final File rootDir) throws IOException { 124 rootDir.mkdirs(); 125 final TarGzEntryNameConverter tarGzEntryNameConverter = this.tarGzEntryNameConverter == null ? defaultEntryNameConverter : this.tarGzEntryNameConverter; 126 final FileFilter fileFilter = this.fileFilter; 127 final FileInputStream fin = new FileInputStream(tarGzFile); 128 try { 129 final TarArchiveInputStream in = new TarArchiveInputStream(new GzipCompressorInputStream(new BufferedInputStream(fin))); 130 try { 131 TarArchiveEntry entry; 132 while (null != (entry = in.getNextTarEntry())) { 133 if(entry.isDirectory()) { 134 // create the directory 135 final File dir = tarGzEntryNameConverter.getFile(rootDir, entry.getName()); 136 if (fileFilter == null || fileFilter.accept(dir)) { 137 if (!dir.exists() && !dir.mkdirs()) 138 throw new IllegalStateException("Could not create directory entry, possibly permission issues: " + dir.getAbsolutePath()); 139 } 140 } 141 else { 142 final File file = tarGzEntryNameConverter.getFile(rootDir, entry.getName()); 143 if (fileFilter == null || fileFilter.accept(file)) { 144 final File dir = file.getParentFile(); 145 if (!dir.isDirectory()) 146 dir.mkdirs(); 147 148 // If the file already exists, we delete it and write into a new one - if possible. 149 // This has the advantage (in GNU/Linux) 150 if (file.isFile()) 151 file.delete(); 152 153 final OutputStream out = new FileOutputStream(file); 154 try { 155 transferStreamData(in, out); 156 } finally { 157 out.close(); 158 } 159 160 if ((entry.getMode() & 0100) != 0 || (entry.getMode() & 010) != 0 || (entry.getMode() & 01) != 0) 161 file.setExecutable(true, false); 162 } 163 } 164 } 165 } finally { 166 in.close(); 167 } 168 } finally { 169 fin.close(); 170 } 171 } 172 173 private void transferStreamData(final InputStream in, final OutputStream out) throws IOException { 174 int len; 175 byte[] buf = new byte[1024 * 16]; 176 while( (len = in.read(buf)) > 0 ) { 177 if (len > 0) 178 out.write(buf, 0, len); 179 } 180 } 181 182}