blob: 07d6ec6e26511cde9072c5e060bb9d5d87bce2c5 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <zircon/compiler.h>
#include <fbl/algorithm.h>
#include <fbl/unique_fd.h>
#include "filesystems.h"
namespace {
bool TestLseekPosition(void) {
BEGIN_TEST;
const char* const filename = "::lseek_position";
fbl::unique_fd fd(open(filename, O_CREAT | O_RDWR));
ASSERT_TRUE(fd);
// File offset initialized to zero.
ASSERT_EQ(lseek(fd.get(), 0, SEEK_CUR), 0);
ASSERT_EQ(lseek(fd.get(), 0, SEEK_SET), 0);
const char* const str = "hello";
const size_t len = strlen(str);
ASSERT_EQ(write(fd.get(), str, len), len);
// After writing, the offset has been updated.
ASSERT_EQ(lseek(fd.get(), 0, SEEK_CUR), len);
ASSERT_EQ(lseek(fd.get(), 0, SEEK_END), len);
// Reset the offset to the start of the file.
ASSERT_EQ(lseek(fd.get(), -len, SEEK_END), 0);
// Read the entire file.
char buf[len + 1];
ASSERT_EQ(read(fd.get(), buf, len), len);
ASSERT_EQ(memcmp(buf, str, len), 0);
// Seek and read part of the file.
ASSERT_EQ(lseek(fd.get(), 1, SEEK_SET), 1);
ASSERT_EQ(read(fd.get(), buf, len - 1), len - 1);
ASSERT_EQ(memcmp(buf, &str[1], len - 1), 0);
ASSERT_EQ(unlink(filename), 0);
END_TEST;
}
bool TestLseekOutOfBounds(void) {
BEGIN_TEST;
const char* const filename = "::lseek_out_of_bounds";
fbl::unique_fd fd(open(filename, O_CREAT | O_RDWR));
ASSERT_TRUE(fd);
const char* const str = "hello";
const size_t len = strlen(str);
ASSERT_EQ(write(fd.get(), str, len), len);
// After writing, the offset has been updated.
ASSERT_EQ(lseek(fd.get(), 0, SEEK_CUR), len);
// Seek beyond the end of the file.
ASSERT_EQ(lseek(fd.get(), 1, SEEK_CUR), len + 1);
ASSERT_EQ(lseek(fd.get(), 2, SEEK_END), len + 2);
ASSERT_EQ(lseek(fd.get(), len + 3, SEEK_SET), len + 3);
// Seek before the start of the file.
ASSERT_EQ(lseek(fd.get(), 0, SEEK_SET), 0);
// Negative seek is not allowed on Fuchsia.
ASSERT_EQ(lseek(fd.get(), -2, SEEK_CUR), -1);
ASSERT_EQ(lseek(fd.get(), -2, SEEK_SET), -1);
ASSERT_EQ(lseek(fd.get(), -(len + 2), SEEK_END), -1);
ASSERT_EQ(unlink(filename), 0);
END_TEST;
}
bool TestLseekZeroFill(void) {
BEGIN_TEST;
const char* const filename = "::lseek_zero_fill";
fbl::unique_fd fd(open(filename, O_CREAT | O_RDWR));
ASSERT_TRUE(fd);
const char* const str = "hello";
const size_t len = strlen(str);
ASSERT_EQ(write(fd.get(), str, len), len);
// After writing, the offset and length have been updated.
ASSERT_EQ(lseek(fd.get(), 0, SEEK_CUR), len);
struct stat st;
ASSERT_EQ(fstat(fd.get(), &st), 0);
ASSERT_EQ(st.st_size, len);
// Seek beyond the end of the file.
size_t zeros = 10;
ASSERT_EQ(lseek(fd.get(), len + zeros, SEEK_SET), static_cast<off_t>(len + zeros));
// This does not change the length of the file.
ASSERT_EQ(fstat(fd.get(), &st), 0);
ASSERT_EQ(st.st_size, len);
// From the POSIX specification:
//
// "Before any action described below is taken, and if nbyte is zero and the
// file is a regular file, the write() function may detect and return
// errors as described below. In the absence of errors, or if error
// detection is not performed, the write() function shall return zero
// and have no other results."
ASSERT_EQ(write(fd.get(), str, 0), 0);
ASSERT_EQ(fstat(fd.get(), &st), 0);
ASSERT_EQ(st.st_size, len);
// Zero-extend the file up to the sentinel value.
char sentinel = 'a';
ASSERT_EQ(write(fd.get(), &sentinel, 1), 1);
ASSERT_EQ(fstat(fd.get(), &st), 0);
ASSERT_EQ(st.st_size, static_cast<off_t>(len + zeros + 1));
// Validate the file contents.
{
char expected[len + zeros + 1];
memcpy(expected, str, len);
memset(&expected[len], 0, zeros);
expected[len + zeros] = 'a';
char buf[len + zeros + 1];
ASSERT_EQ(lseek(fd.get(), 0, SEEK_SET), 0);
ASSERT_EQ(read(fd.get(), buf, sizeof(buf)), static_cast<ssize_t>(sizeof(buf)));
ASSERT_EQ(memcmp(buf, expected, sizeof(expected)), 0);
}
// Truncate and observe the (old) sentinel value has been
// overwritten with zeros.
ASSERT_EQ(ftruncate(fd.get(), len), 0);
zeros *= 2;
ASSERT_EQ(lseek(fd.get(), len + zeros, SEEK_SET), static_cast<off_t>(len + zeros));
ASSERT_EQ(write(fd.get(), &sentinel, 1), 1);
ASSERT_EQ(fstat(fd.get(), &st), 0);
ASSERT_EQ(st.st_size, static_cast<off_t>(len + zeros + 1));
{
char expected[len + zeros + 1];
memcpy(expected, str, len);
memset(&expected[len], 0, zeros);
expected[len + zeros] = 'a';
char buf[len + zeros + 1];
ASSERT_EQ(lseek(fd.get(), 0, SEEK_SET), 0);
ASSERT_EQ(read(fd.get(), buf, sizeof(buf)), static_cast<ssize_t>(sizeof(buf)));
ASSERT_EQ(memcmp(buf, expected, sizeof(expected)), 0);
}
ASSERT_EQ(unlink(filename), 0);
END_TEST;
}
} // namespace
RUN_FOR_ALL_FILESYSTEMS(lseek_tests,
RUN_TEST_MEDIUM(TestLseekPosition)
RUN_TEST_MEDIUM(TestLseekOutOfBounds)
RUN_TEST_MEDIUM(TestLseekZeroFill)
)