Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add FileHandle::truncate and ftruncate #7162

Merged
merged 4 commits into from
Oct 31, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion TESTS/mbed_platform/FileHandle/TestFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class TestFile : public mbed::FileHandle {
~TestFile() {}

enum FunctionName {
fnNone, fnRead, fnWrite, fnSeek, fnClose, fnIsatty
fnNone, fnRead, fnWrite, fnSeek, fnClose, fnIsatty, fnTruncate
};

virtual ssize_t read(void *buffer, size_t size)
Expand Down Expand Up @@ -107,6 +107,24 @@ class TestFile : public mbed::FileHandle {
return 0;
}

virtual off_t size()
{
return _end;
}

virtual int truncate(off_t length)
{
_fnCalled = fnTruncate;
if (!NEW_POS_IS_VALID(length)) {
return -EINVAL;
}
while (_end < length) {
_data[_end++] = 0;
}
_end = length;
return 0;
}


static void resetFunctionCallHistory()
{
Expand Down
99 changes: 78 additions & 21 deletions TESTS/mbed_platform/FileHandle/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -384,17 +384,15 @@ void test_fprintf_fscanf()

/** Test fseek and ftell
*
* Given already opened file is empty
*
* When set the file position indicator via fseek
* Then underneath retargeting layer seek function is called
* fseek return with succeed and ftell return already set position
* ARM library is quite good at optimising out unnecessary calls to underlying
* seek, so only test real non empty files.
*
* Given already opened file is not empty
*
* When set the file position indicator via fseek
* Then underneath retargeting layer seek function is called
* fseek return with succeed and ftell return already set position
* Check actual character read or written.
*
*/
void test_fseek_ftell()
Expand All @@ -413,19 +411,6 @@ void test_fseek_ftell()
ftell_ret = std::ftell(file);
TEST_ASSERT_EQUAL(0, ftell_ret);

TestFile<FS>::resetFunctionCallHistory();
fssek_ret = std::fseek(file, 0, SEEK_CUR);
TEST_ASSERT_EQUAL(0, fssek_ret);

TestFile<FS>::resetFunctionCallHistory();
fssek_ret = std::fseek(file, 0, SEEK_SET);
TEST_ASSERT_EQUAL(0, fssek_ret);

TestFile<FS>::resetFunctionCallHistory();
fssek_ret = std::fseek(file, 0, SEEK_END);
TEST_ASSERT_TRUE(TestFile<FS>::functionCalled(TestFile<FS>::fnSeek));
TEST_ASSERT_EQUAL(0, fssek_ret);

const char *str = "Hello world";
const std::size_t size = std::strlen(str);

Expand All @@ -440,23 +425,94 @@ void test_fseek_ftell()
TEST_ASSERT_EQUAL(0, fssek_ret);
ftell_ret = std::ftell(file);
TEST_ASSERT_EQUAL(5, ftell_ret);
int c = std::fgetc(file);
TEST_ASSERT_TRUE(TestFile<FS>::functionCalled(TestFile<FS>::fnRead));
TEST_ASSERT_EQUAL(c, str[5]);

TestFile<FS>::resetFunctionCallHistory();
fssek_ret = std::fseek(file, -5, SEEK_CUR);
fssek_ret = std::fseek(file, -6, SEEK_CUR);
TEST_ASSERT_EQUAL(0, fssek_ret);
ftell_ret = std::ftell(file);
TEST_ASSERT_EQUAL(0, ftell_ret);
c = std::fgetc(file);
TEST_ASSERT_TRUE(TestFile<FS>::functionCalled(TestFile<FS>::fnRead));
TEST_ASSERT_EQUAL(c, str[0]);

TestFile<FS>::resetFunctionCallHistory();
fssek_ret = std::fseek(file, 0, SEEK_END);
TEST_ASSERT_TRUE(TestFile<FS>::functionCalled(TestFile<FS>::fnSeek));
TEST_ASSERT_EQUAL(0, fssek_ret);
ftell_ret = std::ftell(file);
TEST_ASSERT_EQUAL(size, ftell_ret);
c = std::fputc('!', file);
TEST_ASSERT_TRUE(TestFile<FS>::functionCalled(TestFile<FS>::fnWrite));
TEST_ASSERT_EQUAL(c, '!');
TEST_ASSERT_EQUAL(fh.size(), size + 1);

std::fclose(file);
}

/** Test ftruncate and fstat (st_size)
*
* Check we get EBADF for illegal handles
*
* Given already opened file is empty
*
* Check initial size is returned as 0
* Call ftruncate with negative value - check our EINVAL is passed back
* Call ftruncate with positive value to increase size - check no error return
* Check fstat st_size now reads back as the value we set.
* Call ftruncate with smaller positive value to decrease size - check no error return
* Check fstat st_size now reads back as the value we set.
*/
void test_ftruncate_fstat()
{
int fildes;
int ret;
struct stat st;
const uint32_t FS = 128;
TestFile<FS> fh;

ret = ftruncate(12345678, 24);
TEST_ASSERT_EQUAL(-1, ret);
TEST_ASSERT_EQUAL(EBADF, errno);

ret = fstat(12345678, &st);
TEST_ASSERT_EQUAL(-1, ret);
TEST_ASSERT_EQUAL(EBADF, errno);

fildes = bind_to_fd(&fh);
TEST_ASSERT_TRUE(fildes >= 0);

ret = fstat(fildes, &st);
TEST_ASSERT_EQUAL(0, ret);
TEST_ASSERT_EQUAL(0, st.st_size);

TestFile<FS>::resetFunctionCallHistory();
ret = ftruncate(fildes, -3);
TEST_ASSERT_TRUE(TestFile<FS>::functionCalled(TestFile<FS>::fnTruncate));
TEST_ASSERT_EQUAL(-1, ret);
TEST_ASSERT_EQUAL(EINVAL, errno);

TestFile<FS>::resetFunctionCallHistory();
ret = ftruncate(fildes, 24);
TEST_ASSERT_TRUE(TestFile<FS>::functionCalled(TestFile<FS>::fnTruncate));
TEST_ASSERT_EQUAL(0, ret);

ret = fstat(fildes, &st);
TEST_ASSERT_EQUAL(0, ret);
TEST_ASSERT_EQUAL(24, st.st_size);

ret = ftruncate(fildes, 12);
TEST_ASSERT_TRUE(TestFile<FS>::functionCalled(TestFile<FS>::fnTruncate));
TEST_ASSERT_EQUAL(0, ret);

ret = fstat(fildes, &st);
TEST_ASSERT_EQUAL(0, ret);
TEST_ASSERT_EQUAL(12, st.st_size);

close(fildes);
}

utest::v1::status_t test_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(10, "default_auto");
Expand All @@ -469,7 +525,8 @@ Case cases[] = {
Case("Test fputc/fgetc", test_fputc_fgetc),
Case("Test fputs/fgets", test_fputs_fgets),
Case("Test fprintf/fscanf", test_fprintf_fscanf),
Case("Test fseek/ftell", test_fseek_ftell)
Case("Test fseek/ftell", test_fseek_ftell),
Case("Test ftruncate/fstat", test_ftruncate_fstat)
};

utest::v1::Specification specification(test_setup, cases);
Expand Down
6 changes: 6 additions & 0 deletions features/storage/filesystem/File.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,10 @@ off_t File::size()
return _fs->file_size(_file);
}

int File::truncate(off_t length)
{
MBED_ASSERT(_fs);
return _fs->file_truncate(_file, length);
}

} // namespace mbed
12 changes: 12 additions & 0 deletions features/storage/filesystem/File.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,18 @@ class File : public FileHandle {
*/
virtual off_t size();

/** Truncate or extend a file.
*
* The file's length is set to the specified value. The seek pointer is
* not changed. If the file is extended, the extended area appears as if
* it were zero-filled.
*
* @param length The requested new length for the file
*
* @return Zero on success, negative error code on failure
*/
virtual int truncate(off_t length);

private:
FileSystem *_fs;
fs_file_t _file;
Expand Down
5 changes: 5 additions & 0 deletions features/storage/filesystem/FileSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ off_t FileSystem::file_size(fs_file_t file)
return size;
}

int FileSystem::file_truncate(fs_file_t file, off_t length)
{
return -ENOSYS;
}

int FileSystem::dir_open(fs_dir_t *dir, const char *path)
{
return -ENOSYS;
Expand Down
13 changes: 13 additions & 0 deletions features/storage/filesystem/FileSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,19 @@ class FileSystem : public FileSystemLike {
*/
virtual off_t file_size(fs_file_t file);

/** Truncate or extend a file.
*
* The file's length is set to the specified value. The seek pointer is
* not changed. If the file is extended, the extended area appears as if
* it were zero-filled.
*
* @param file File handle
* @param length The requested new length for the file
*
* @return Zero on success, negative error code on failure
*/
virtual int file_truncate(fs_file_t file, off_t length);

/** Open a directory on the filesystem
*
* @param dir Destination for the handle to the directory
Expand Down
31 changes: 31 additions & 0 deletions features/storage/filesystem/fat/FATFileSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,37 @@ off_t FATFileSystem::file_size(fs_file_t file)
return res;
}

int FATFileSystem::file_truncate(fs_file_t file, off_t length)
{
FIL *fh = static_cast<FIL*>(file);

lock();
// save current position
FSIZE_t oldoff = f_tell(fh);

// seek to new file size and truncate
FRESULT res = f_lseek(fh, length);
if (res) {
unlock();
return fat_error_remap(res);
}

res = f_truncate(fh);
if (res) {
unlock();
return fat_error_remap(res);
}

// restore old position
res = f_lseek(fh, oldoff);
if (res) {
unlock();
return fat_error_remap(res);
}

return 0;
}


////// Dir operations //////
int FATFileSystem::dir_open(fs_dir_t *dir, const char *path)
Expand Down
13 changes: 13 additions & 0 deletions features/storage/filesystem/fat/FATFileSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,19 @@ class FATFileSystem : public mbed::FileSystem {
*/
virtual off_t file_size(mbed::fs_file_t file);

/** Truncate or extend a file.
*
* The file's length is set to the specified value. The seek pointer is
* not changed. If the file is extended, the extended area appears as if
* it were zero-filled.
*
* @param file File handle
* @param length The requested new length for the file
*
* @return Zero on success, negative error code on failure
*/
virtual int file_truncate(mbed::fs_file_t file, off_t length);

/** Open a directory on the filesystem
*
* @param dir Destination for the handle to the directory
Expand Down
11 changes: 11 additions & 0 deletions features/storage/filesystem/littlefs/LittleFileSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,17 @@ off_t LittleFileSystem::file_size(fs_file_t file)
return lfs_toerror(res);
}

int LittleFileSystem::file_truncate(fs_file_t file, off_t length)
{
lfs_file_t *f = (lfs_file_t *)file;
_mutex.lock();
LFS_INFO("file_truncate(%p)", file);
int err = lfs_file_truncate(&_lfs, f, length);
LFS_INFO("file_truncate -> %d", lfs_toerror(err));
_mutex.unlock();
return lfs_toerror(err);
}


////// Dir operations //////
int LittleFileSystem::dir_open(fs_dir_t *dir, const char *path)
Expand Down
13 changes: 13 additions & 0 deletions features/storage/filesystem/littlefs/LittleFileSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,19 @@ class LittleFileSystem : public mbed::FileSystem {
*/
virtual off_t file_size(mbed::fs_file_t file);

/** Truncate or extend a file.
*
* The file's length is set to the specified value. The seek pointer is
* not changed. If the file is extended, the extended area appears as if
* it were zero-filled.
*
* @param file File handle
* @param length The requested new length for the file
*
* @return Zero on success, negative error code on failure
*/
virtual int file_truncate(mbed::fs_file_t file, off_t length);

/** Open a directory on the filesystem
*
* @param dir Destination for the handle to the directory
Expand Down
15 changes: 15 additions & 0 deletions platform/FileHandle.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,21 @@ class FileHandle : private NonCopyable<FileHandle> {
*/
virtual off_t size();

/** Truncate or extend a file.
*
* The file's length is set to the specified value. The seek pointer is
* not changed. If the file is extended, the extended area appears as if
* it were zero-filled.
*
* @param length The requested new length for the file
*
* @return Zero on success, negative error code on failure
*/
virtual int truncate(off_t length)
kjbracey marked this conversation as resolved.
Show resolved Hide resolved
{
return -EINVAL;
}

/** Move the file position to a given offset from a given location.
*
* @param offset The offset from whence to move to
Expand Down
17 changes: 17 additions & 0 deletions platform/mbed_retarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,23 @@ extern "C" off_t lseek(int fildes, off_t offset, int whence)
return off;
}

extern "C" int ftruncate(int fildes, off_t length)
{
FileHandle* fhc = get_fhc(fildes);
if (fhc == NULL) {
errno = EBADF;
return -1;
}

int err = fhc->truncate(length);
if (err < 0) {
errno = -err;
return -1;
} else {
return 0;
}
}

#ifdef __ARMCC_VERSION
extern "C" int PREFIX(_ensure)(FILEHANDLE fh)
{
Expand Down
1 change: 1 addition & 0 deletions platform/mbed_retarget.h
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,7 @@ extern "C" {
ssize_t write(int fildes, const void *buf, size_t nbyte);
ssize_t read(int fildes, void *buf, size_t nbyte);
off_t lseek(int fildes, off_t offset, int whence);
int ftruncate(int fildes, off_t length);
int isatty(int fildes);
int fsync(int fildes);
int fstat(int fildes, struct stat *st);
Expand Down