private String putDirectoryBlob(final String containerName, final Blob blob) throws IOException { String blobKey = blob.getMetadata().getName(); ContentMetadata metadata = blob.getMetadata().getContentMetadata(); Long contentLength = metadata.getContentLength(); if (contentLength != null && contentLength != 0) { throw new IllegalArgumentException("Directory blob cannot have content: " + blobKey); } File outputFile = getFileForBlobKey(containerName, blobKey); Path outputPath = outputFile.toPath(); if (!outputFile.isDirectory() && !outputFile.mkdirs()) { throw new IOException("Unable to mkdir: " + outputPath); } UserDefinedFileAttributeView view = getUserDefinedFileAttributeView(outputPath); if (view != null) { try { view.write(XATTR_CONTENT_MD5, ByteBuffer.wrap(DIRECTORY_MD5)); writeCommonMetadataAttr(view, blob); } catch (IOException e) { logger.debug("xattrs not supported on %s", outputPath); } } else { logger.warn("xattr not supported on %s", blobKey); } return base16().lowerCase().encode(DIRECTORY_MD5); }
// "randomize" the file attributes of the given file. static void randomizeAttributes(Path file) throws IOException { String os = System.getProperty("os.name"); boolean isWindows = os.startsWith("Windows"); boolean isUnix = os.equals("SunOS") || os.equals("Linux"); boolean isDirectory = isDirectory(file, NOFOLLOW_LINKS); if (isUnix) { Set<PosixFilePermission> perms = getPosixFilePermissions(file, NOFOLLOW_LINKS); PosixFilePermission[] toChange = { PosixFilePermission.GROUP_READ, PosixFilePermission.GROUP_WRITE, PosixFilePermission.GROUP_EXECUTE, PosixFilePermission.OTHERS_READ, PosixFilePermission.OTHERS_WRITE, PosixFilePermission.OTHERS_EXECUTE }; for (PosixFilePermission perm : toChange) { if (heads()) { perms.add(perm); } else { perms.remove(perm); } } setPosixFilePermissions(file, perms); } if (isWindows) { DosFileAttributeView view = getFileAttributeView(file, DosFileAttributeView.class, NOFOLLOW_LINKS); // only set or unset the hidden attribute view.setHidden(heads()); } boolean addUserDefinedFileAttributes = heads() && getFileStore(file).supportsFileAttributeView("xattr"); // remove this when copying a direcory copies its named streams if (isWindows && isDirectory) addUserDefinedFileAttributes = false; if (addUserDefinedFileAttributes) { UserDefinedFileAttributeView view = getFileAttributeView(file, UserDefinedFileAttributeView.class); int n = rand.nextInt(16); while (n > 0) { byte[] value = new byte[1 + rand.nextInt(100)]; view.write("user." + Integer.toString(n), ByteBuffer.wrap(value)); n--; } } }
public static void writeFileKey(Path filePath, String fileKey) { if (!OSDetector.isWindows()) { return; } File file = filePath.toFile(); if (!file.canWrite()) { file.setWritable(true); } UserDefinedFileAttributeView userDefinedFileAttributeView = Files.getFileAttributeView(filePath, UserDefinedFileAttributeView.class); try { userDefinedFileAttributeView.write("fileKey", _CHARSET.encode(CharBuffer.wrap(fileKey))); } catch (Exception e) { _logger.error(e.getMessage(), e); } }
private void writeCommonMetadataAttr(UserDefinedFileAttributeView view, Blob blob) throws IOException { ContentMetadata metadata = blob.getMetadata().getContentMetadata(); writeStringAttributeIfPresent(view, XATTR_CACHE_CONTROL, metadata.getCacheControl()); writeStringAttributeIfPresent( view, XATTR_CONTENT_DISPOSITION, metadata.getContentDisposition()); writeStringAttributeIfPresent(view, XATTR_CONTENT_ENCODING, metadata.getContentEncoding()); writeStringAttributeIfPresent(view, XATTR_CONTENT_LANGUAGE, metadata.getContentLanguage()); writeStringAttributeIfPresent(view, XATTR_CONTENT_TYPE, metadata.getContentType()); Date expires = metadata.getExpires(); if (expires != null) { ByteBuffer buf = ByteBuffer.allocate(Longs.BYTES).putLong(expires.getTime()); buf.flip(); view.write(XATTR_EXPIRES, buf); } for (Map.Entry<String, String> entry : blob.getMetadata().getUserMetadata().entrySet()) { writeStringAttributeIfPresent( view, XATTR_USER_METADATA_PREFIX + entry.getKey(), entry.getValue()); } }
protected static void doWriteFileKey(Path filePath, String fileKey) { if (hasFileKey(filePath, Long.parseLong(fileKey))) { return; } if (OSDetector.isApple()) { Xattrj xattrj = getXattrj(); if (xattrj == null) { return; } File file = filePath.toFile(); if (!file.canWrite()) { file.setWritable(true); } xattrj.writeAttribute(file, "fileKey", fileKey); } else { File file = filePath.toFile(); if (!file.canWrite()) { file.setWritable(true); } UserDefinedFileAttributeView userDefinedFileAttributeView = Files.getFileAttributeView(filePath, UserDefinedFileAttributeView.class); try { userDefinedFileAttributeView.write("fileKey", _CHARSET.encode(CharBuffer.wrap(fileKey))); } catch (Exception e) { _logger.error(e.getMessage(), e); } } }
/** Write an filesystem attribute, if its value is non-null. */ private static void writeStringAttributeIfPresent( UserDefinedFileAttributeView view, String name, String value) throws IOException { if (value != null) { view.write(name, ByteBuffer.wrap(value.getBytes(StandardCharsets.UTF_8))); } }
@Override public String putBlob(final String containerName, final Blob blob) throws IOException { String blobKey = blob.getMetadata().getName(); Payload payload = blob.getPayload(); filesystemContainerNameValidator.validate(containerName); filesystemBlobKeyValidator.validate(blobKey); if (getDirectoryBlobSuffix(blobKey) != null) { return putDirectoryBlob(containerName, blob); } File outputFile = getFileForBlobKey(containerName, blobKey); // TODO: should we use a known suffix to filter these out during list? String tmpBlobName = blobKey + "-" + UUID.randomUUID(); File tmpFile = getFileForBlobKey(containerName, tmpBlobName); Path tmpPath = tmpFile.toPath(); HashingInputStream his = null; try { Files.createParentDirs(tmpFile); his = new HashingInputStream(Hashing.md5(), payload.openStream()); long actualSize = Files.asByteSink(tmpFile).writeFrom(his); Long expectedSize = blob.getMetadata().getContentMetadata().getContentLength(); if (expectedSize != null && actualSize != expectedSize) { throw new IOException( "Content-Length mismatch, actual: " + actualSize + " expected: " + expectedSize); } HashCode actualHashCode = his.hash(); HashCode expectedHashCode = payload.getContentMetadata().getContentMD5AsHashCode(); if (expectedHashCode != null && !actualHashCode.equals(expectedHashCode)) { throw new IOException( "MD5 hash code mismatch, actual: " + actualHashCode + " expected: " + expectedHashCode); } payload.getContentMetadata().setContentMD5(actualHashCode); if (outputFile.exists()) { delete(outputFile); } UserDefinedFileAttributeView view = getUserDefinedFileAttributeView(tmpPath); if (view != null) { try { view.write(XATTR_CONTENT_MD5, ByteBuffer.wrap(actualHashCode.asBytes())); writeCommonMetadataAttr(view, blob); } catch (IOException e) { logger.debug("xattrs not supported on %s", tmpPath); } } setBlobAccess(containerName, tmpBlobName, BlobAccess.PRIVATE); if (!tmpFile.renameTo(outputFile)) { throw new RuntimeException("Could not rename file " + tmpFile + " to " + outputFile); } return base16().lowerCase().encode(actualHashCode.asBytes()); } catch (IOException ex) { if (tmpFile != null) { try { delete(tmpFile); } catch (IOException e) { logger.debug("Could not delete %s: %s", tmpFile, e); } } throw ex; } finally { closeQuietly(his); if (payload != null) { payload.release(); } } }