From 6cc8e38db5b26bdd02bc6bc1c9684db2593eec25 Mon Sep 17 00:00:00 2001 From: cnauroth Date: Wed, 12 Aug 2015 16:44:53 -0700 Subject: [PATCH] HADOOP-12258. Need translate java.nio.file.NoSuchFileException to FileNotFoundException to avoid regression. Contributed by Zhihai Xu. --- .../hadoop-common/CHANGES.txt | 3 + .../apache/hadoop/fs/RawLocalFileSystem.java | 36 +++++++---- .../AbstractContractGetFileStatusTest.java | 61 +++++++++++++++++++ .../AbstractContractSetTimesTest.java | 61 +++++++++++++++++++ .../hadoop/fs/contract/ContractOptions.java | 12 ++++ .../TestLocalFSContractGetFileStatus.java | 33 ++++++++++ .../localfs/TestLocalFSContractSetTimes.java | 33 ++++++++++ .../TestRawlocalContractGetFileStatus.java | 33 ++++++++++ .../TestRawlocalContractSetTimes.java | 33 ++++++++++ .../src/test/resources/contract/localfs.xml | 10 +++ .../src/test/resources/contract/rawlocal.xml | 10 +++ .../hdfs/TestHDFSContractGetFileStatus.java | 46 ++++++++++++++ .../hdfs/TestHDFSContractSetTimes.java | 45 ++++++++++++++ .../src/test/resources/contract/hdfs.xml | 10 +++ 14 files changed, 415 insertions(+), 11 deletions(-) create mode 100644 hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/contract/AbstractContractGetFileStatusTest.java create mode 100644 hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/contract/AbstractContractSetTimesTest.java create mode 100644 hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/contract/localfs/TestLocalFSContractGetFileStatus.java create mode 100644 hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/contract/localfs/TestLocalFSContractSetTimes.java create mode 100644 hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/contract/rawlocal/TestRawlocalContractGetFileStatus.java create mode 100644 hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/contract/rawlocal/TestRawlocalContractSetTimes.java create mode 100644 hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/fs/contract/hdfs/TestHDFSContractGetFileStatus.java create mode 100644 hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/fs/contract/hdfs/TestHDFSContractSetTimes.java diff --git a/hadoop-common-project/hadoop-common/CHANGES.txt b/hadoop-common-project/hadoop-common/CHANGES.txt index e9be2e0816..78f12e4876 100644 --- a/hadoop-common-project/hadoop-common/CHANGES.txt +++ b/hadoop-common-project/hadoop-common/CHANGES.txt @@ -1056,6 +1056,9 @@ Release 2.8.0 - UNRELEASED HADOOP-12302. Fix native compilation on Windows after HADOOP-7824 (Vinayakumar B via Colin P. McCabe) + HADOOP-12258. Need translate java.nio.file.NoSuchFileException to + FileNotFoundException to avoid regression. (Zhihai Xu via cnauroth) + Release 2.7.2 - UNRELEASED INCOMPATIBLE CHANGES diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/RawLocalFileSystem.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/RawLocalFileSystem.java index 4728dbe4f2..8ff65fad77 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/RawLocalFileSystem.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/RawLocalFileSystem.java @@ -34,6 +34,7 @@ import java.net.URI; import java.nio.ByteBuffer; import java.nio.file.Files; +import java.nio.file.NoSuchFileException; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributeView; import java.nio.file.attribute.FileTime; @@ -650,13 +651,22 @@ static class DeprecatedRawLocalFileStatus extends FileStatus { private boolean isPermissionLoaded() { return !super.getOwner().isEmpty(); } - + + private static long getLastAccessTime(File f) throws IOException { + long accessTime; + try { + accessTime = Files.readAttributes(f.toPath(), + BasicFileAttributes.class).lastAccessTime().toMillis(); + } catch (NoSuchFileException e) { + throw new FileNotFoundException("File " + f + " does not exist"); + } + return accessTime; + } + DeprecatedRawLocalFileStatus(File f, long defaultBlockSize, FileSystem fs) throws IOException { super(f.length(), f.isDirectory(), 1, defaultBlockSize, - f.lastModified(), - Files.readAttributes(f.toPath(), - BasicFileAttributes.class).lastAccessTime().toMillis(), + f.lastModified(), getLastAccessTime(f), null, null, null, new Path(f.getPath()).makeQualified(fs.getUri(), fs.getWorkingDirectory())); @@ -773,17 +783,21 @@ public void setPermission(Path p, FsPermission permission) * Sets the {@link Path}'s last modified time and last access time to * the given valid times. * - * @param mtime the modification time to set (only if greater than zero). - * @param atime the access time to set (only if greater than zero). + * @param mtime the modification time to set (only if no less than zero). + * @param atime the access time to set (only if no less than zero). * @throws IOException if setting the times fails. */ @Override public void setTimes(Path p, long mtime, long atime) throws IOException { - BasicFileAttributeView view = Files.getFileAttributeView( - pathToFile(p).toPath(), BasicFileAttributeView.class); - FileTime fmtime = (mtime >= 0) ? FileTime.fromMillis(mtime) : null; - FileTime fatime = (atime >= 0) ? FileTime.fromMillis(atime) : null; - view.setTimes(fmtime, fatime, null); + try { + BasicFileAttributeView view = Files.getFileAttributeView( + pathToFile(p).toPath(), BasicFileAttributeView.class); + FileTime fmtime = (mtime >= 0) ? FileTime.fromMillis(mtime) : null; + FileTime fatime = (atime >= 0) ? FileTime.fromMillis(atime) : null; + view.setTimes(fmtime, fatime, null); + } catch (NoSuchFileException e) { + throw new FileNotFoundException("File " + p + " does not exist"); + } } @Override diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/contract/AbstractContractGetFileStatusTest.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/contract/AbstractContractGetFileStatusTest.java new file mode 100644 index 0000000000..7ed375e648 --- /dev/null +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/contract/AbstractContractGetFileStatusTest.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.hadoop.fs.contract; + +import java.io.FileNotFoundException; + +import org.apache.hadoop.fs.FileStatus; +import org.apache.hadoop.fs.Path; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Test getFileStatus -if supported + */ +public abstract class AbstractContractGetFileStatusTest extends + AbstractFSContractTestBase { + private static final Logger LOG = + LoggerFactory.getLogger(AbstractContractGetFileStatusTest.class); + + private Path testPath; + private Path target; + + @Override + public void setup() throws Exception { + super.setup(); + skipIfUnsupported(SUPPORTS_GETFILESTATUS); + + //delete the test directory + testPath = path("test"); + target = new Path(testPath, "target"); + } + + @Test + public void testGetFileStatusNonexistentFile() throws Throwable { + try { + FileStatus status = getFileSystem().getFileStatus(target); + //got here: trouble + fail("expected a failure"); + } catch (FileNotFoundException e) { + //expected + handleExpectedException(e); + } + } +} diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/contract/AbstractContractSetTimesTest.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/contract/AbstractContractSetTimesTest.java new file mode 100644 index 0000000000..2cb23487fb --- /dev/null +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/contract/AbstractContractSetTimesTest.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.hadoop.fs.contract; + +import java.io.FileNotFoundException; + +import org.apache.hadoop.fs.Path; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Test setTimes -if supported + */ +public abstract class AbstractContractSetTimesTest extends + AbstractFSContractTestBase { + private static final Logger LOG = + LoggerFactory.getLogger(AbstractContractSetTimesTest.class); + + private Path testPath; + private Path target; + + @Override + public void setup() throws Exception { + super.setup(); + skipIfUnsupported(SUPPORTS_SETTIMES); + + //delete the test directory + testPath = path("test"); + target = new Path(testPath, "target"); + } + + @Test + public void testSetTimesNonexistentFile() throws Throwable { + try { + long time = System.currentTimeMillis(); + getFileSystem().setTimes(target, time, time); + //got here: trouble + fail("expected a failure"); + } catch (FileNotFoundException e) { + //expected + handleExpectedException(e); + } + } +} diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/contract/ContractOptions.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/contract/ContractOptions.java index d9427c6c9d..56caced7b7 100644 --- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/contract/ContractOptions.java +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/contract/ContractOptions.java @@ -92,6 +92,18 @@ public interface ContractOptions { */ String SUPPORTS_APPEND = "supports-append"; + /** + * Flag to indicate that setTimes is supported. + * @{value} + */ + String SUPPORTS_SETTIMES = "supports-settimes"; + + /** + * Flag to indicate that getFileStatus is supported. + * @{value} + */ + String SUPPORTS_GETFILESTATUS = "supports-getfilestatus"; + /** * Flag to indicate that renames are atomic * @{value} diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/contract/localfs/TestLocalFSContractGetFileStatus.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/contract/localfs/TestLocalFSContractGetFileStatus.java new file mode 100644 index 0000000000..8eb1047857 --- /dev/null +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/contract/localfs/TestLocalFSContractGetFileStatus.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.hadoop.fs.contract.localfs; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.contract.AbstractContractGetFileStatusTest; +import org.apache.hadoop.fs.contract.AbstractFSContract; + +public class TestLocalFSContractGetFileStatus extends + AbstractContractGetFileStatusTest { + + @Override + protected AbstractFSContract createContract(Configuration conf) { + return new LocalFSContract(conf); + } + +} diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/contract/localfs/TestLocalFSContractSetTimes.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/contract/localfs/TestLocalFSContractSetTimes.java new file mode 100644 index 0000000000..3766205ae3 --- /dev/null +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/contract/localfs/TestLocalFSContractSetTimes.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.hadoop.fs.contract.localfs; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.contract.AbstractContractSetTimesTest; +import org.apache.hadoop.fs.contract.AbstractFSContract; + +public class TestLocalFSContractSetTimes extends + AbstractContractSetTimesTest { + + @Override + protected AbstractFSContract createContract(Configuration conf) { + return new LocalFSContract(conf); + } + +} diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/contract/rawlocal/TestRawlocalContractGetFileStatus.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/contract/rawlocal/TestRawlocalContractGetFileStatus.java new file mode 100644 index 0000000000..e6fcec5dab --- /dev/null +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/contract/rawlocal/TestRawlocalContractGetFileStatus.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.hadoop.fs.contract.rawlocal; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.contract.AbstractContractGetFileStatusTest; +import org.apache.hadoop.fs.contract.AbstractFSContract; + +public class TestRawlocalContractGetFileStatus extends + AbstractContractGetFileStatusTest { + + @Override + protected AbstractFSContract createContract(Configuration conf) { + return new RawlocalFSContract(conf); + } + +} diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/contract/rawlocal/TestRawlocalContractSetTimes.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/contract/rawlocal/TestRawlocalContractSetTimes.java new file mode 100644 index 0000000000..b94f3ac4e2 --- /dev/null +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/contract/rawlocal/TestRawlocalContractSetTimes.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.hadoop.fs.contract.rawlocal; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.contract.AbstractContractSetTimesTest; +import org.apache.hadoop.fs.contract.AbstractFSContract; + +public class TestRawlocalContractSetTimes extends + AbstractContractSetTimesTest { + + @Override + protected AbstractFSContract createContract(Configuration conf) { + return new RawlocalFSContract(conf); + } + +} diff --git a/hadoop-common-project/hadoop-common/src/test/resources/contract/localfs.xml b/hadoop-common-project/hadoop-common/src/test/resources/contract/localfs.xml index 38d68b3347..b2e068c41e 100644 --- a/hadoop-common-project/hadoop-common/src/test/resources/contract/localfs.xml +++ b/hadoop-common-project/hadoop-common/src/test/resources/contract/localfs.xml @@ -111,4 +111,14 @@ case sensitivity and permission options are determined at run time from OS type false + + fs.contract.supports-settimes + true + + + + fs.contract.supports-getfilestatus + true + + \ No newline at end of file diff --git a/hadoop-common-project/hadoop-common/src/test/resources/contract/rawlocal.xml b/hadoop-common-project/hadoop-common/src/test/resources/contract/rawlocal.xml index 4b26cb3195..b51f3d0edb 100644 --- a/hadoop-common-project/hadoop-common/src/test/resources/contract/rawlocal.xml +++ b/hadoop-common-project/hadoop-common/src/test/resources/contract/rawlocal.xml @@ -98,4 +98,14 @@ true + + fs.contract.supports-settimes + true + + + + fs.contract.supports-getfilestatus + true + + \ No newline at end of file diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/fs/contract/hdfs/TestHDFSContractGetFileStatus.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/fs/contract/hdfs/TestHDFSContractGetFileStatus.java new file mode 100644 index 0000000000..d81d3c200f --- /dev/null +++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/fs/contract/hdfs/TestHDFSContractGetFileStatus.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.hadoop.fs.contract.hdfs; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.contract.AbstractContractGetFileStatusTest; +import org.apache.hadoop.fs.contract.AbstractFSContract; +import org.junit.AfterClass; +import org.junit.BeforeClass; + +import java.io.IOException; + +public class TestHDFSContractGetFileStatus extends + AbstractContractGetFileStatusTest { + + @BeforeClass + public static void createCluster() throws IOException { + HDFSContract.createCluster(); + } + + @AfterClass + public static void teardownCluster() throws IOException { + HDFSContract.destroyCluster(); + } + + @Override + protected AbstractFSContract createContract(Configuration conf) { + return new HDFSContract(conf); + } +} diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/fs/contract/hdfs/TestHDFSContractSetTimes.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/fs/contract/hdfs/TestHDFSContractSetTimes.java new file mode 100644 index 0000000000..4899189b01 --- /dev/null +++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/fs/contract/hdfs/TestHDFSContractSetTimes.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.hadoop.fs.contract.hdfs; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.contract.AbstractContractSetTimesTest; +import org.apache.hadoop.fs.contract.AbstractFSContract; +import org.junit.AfterClass; +import org.junit.BeforeClass; + +import java.io.IOException; + +public class TestHDFSContractSetTimes extends AbstractContractSetTimesTest { + + @BeforeClass + public static void createCluster() throws IOException { + HDFSContract.createCluster(); + } + + @AfterClass + public static void teardownCluster() throws IOException { + HDFSContract.destroyCluster(); + } + + @Override + protected AbstractFSContract createContract(Configuration conf) { + return new HDFSContract(conf); + } +} diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/resources/contract/hdfs.xml b/hadoop-hdfs-project/hadoop-hdfs/src/test/resources/contract/hdfs.xml index 51f479a66a..8555bd95b4 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/test/resources/contract/hdfs.xml +++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/resources/contract/hdfs.xml @@ -91,4 +91,14 @@ true + + fs.contract.supports-settimes + true + + + + fs.contract.supports-getfilestatus + true + + \ No newline at end of file