/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.facebook.presto.raptor.util;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.RawLocalFileSystem;
import org.apache.hadoop.util.Progressable;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public final class SyncingFileSystem
        extends RawLocalFileSystem
{
    public SyncingFileSystem(Configuration configuration)
            throws IOException
    {
        initialize(getUri(), configuration);
    }

    @Override
    public FSDataOutputStream create(Path path, boolean overwrite, int bufferSize, short replication, long blockSize, Progressable progress)
            throws IOException
    {
        if (exists(path) && !overwrite) {
            throw new IOException("file already exists: " + path);
        }
        Path parent = path.getParent();
        if ((parent != null) && !mkdirs(parent)) {
            throw new IOException("mkdirs failed to create " + parent.toString());
        }
        return new FSDataOutputStream(
                new BufferedOutputStream(new LocalFileOutputStream(pathToFile(path)), bufferSize),
                statistics);
    }

    private static class LocalFileOutputStream
            extends OutputStream
    {
        private final FileOutputStream out;
        private boolean closed;

        private LocalFileOutputStream(File file)
                throws IOException
        {
            this.out = new FileOutputStream(file);
        }

        @Override
        public void close()
                throws IOException
        {
            if (closed) {
                return;
            }
            closed = true;

            flush();
            out.getFD().sync();
            out.close();
        }

        @Override
        public void flush()
                throws IOException
        {
            out.flush();
        }

        @Override
        public void write(byte[] b, int off, int len)
                throws IOException
        {
            out.write(b, off, len);
        }

        @Override
        public void write(int b)
                throws IOException
        {
            out.write(b);
        }
    }
}

@@ -597,16 +597,50 @@
 .util;%0A%0A
+import io.airlift.slice.XxHash64;%0A
 import o
@@ -909,16 +909,48 @@
 o.File;%0A
+import java.io.FileInputStream;%0A
 import j
@@ -998,24 +998,52 @@
 OException;%0A
+import java.io.InputStream;%0A
 import java.
@@ -1060,16 +1060,65 @@
 tream;%0A%0A
+import static java.util.Objects.requireNonNull;%0A%0A
 public f
@@ -2081,24 +2081,163 @@
 tream%0A    %7B%0A
+        private final byte%5B%5D oneByte = new byte%5B1%5D;%0A        private final XxHash64 hash = new XxHash64();%0A        private final File file;%0A
         priv
@@ -2387,32 +2387,94 @@
 ption%0A        %7B%0A
+            this.file = requireNonNull(file, %22file is null%22);%0A
             this
@@ -2779,16 +2779,318 @@
 close();
+%0A%0A            // extremely paranoid code to detect a broken local file system%0A            try (InputStream in = new FileInputStream(file)) %7B%0A                if (hash.hash() != XxHash64.hash(in)) %7B%0A                    throw new IOException(%22File is corrupt after write%22);%0A                %7D%0A            %7D
 %0A       
@@ -3373,34 +3373,131 @@
 , len);%0A        
-%7D%0A
+    hash.update(b, off, len);%0A        %7D%0A%0A        @SuppressWarnings(%22NumericCastThatLosesPrecision%22)
 %0A        @Overri
@@ -3590,26 +3590,78 @@
        o
-ut.write(b
+neByte%5B0%5D = (byte) (b & 0xFF);%0A            write(oneByte, 0, 1
 );%0A     

Verify local Raptor files after writing to disk