Skip to content

Commit eea338c

Browse files
author
Cruz Monrreal
authored
Merge pull request #7162 from kjbracey-arm/truncate
Add FileHandle::truncate and ftruncate
2 parents 5b25b66 + e22e9cd commit eea338c

File tree

13 files changed

+234
-22
lines changed

13 files changed

+234
-22
lines changed

TESTS/mbed_platform/FileHandle/TestFile.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class TestFile : public mbed::FileHandle {
3030
~TestFile() {}
3131

3232
enum FunctionName {
33-
fnNone, fnRead, fnWrite, fnSeek, fnClose, fnIsatty
33+
fnNone, fnRead, fnWrite, fnSeek, fnClose, fnIsatty, fnTruncate
3434
};
3535

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

110+
virtual off_t size()
111+
{
112+
return _end;
113+
}
114+
115+
virtual int truncate(off_t length)
116+
{
117+
_fnCalled = fnTruncate;
118+
if (!NEW_POS_IS_VALID(length)) {
119+
return -EINVAL;
120+
}
121+
while (_end < length) {
122+
_data[_end++] = 0;
123+
}
124+
_end = length;
125+
return 0;
126+
}
127+
110128

111129
static void resetFunctionCallHistory()
112130
{

TESTS/mbed_platform/FileHandle/main.cpp

Lines changed: 78 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -384,17 +384,15 @@ void test_fprintf_fscanf()
384384

385385
/** Test fseek and ftell
386386
*
387-
* Given already opened file is empty
388-
*
389-
* When set the file position indicator via fseek
390-
* Then underneath retargeting layer seek function is called
391-
* fseek return with succeed and ftell return already set position
387+
* ARM library is quite good at optimising out unnecessary calls to underlying
388+
* seek, so only test real non empty files.
392389
*
393390
* Given already opened file is not empty
394391
*
395392
* When set the file position indicator via fseek
396393
* Then underneath retargeting layer seek function is called
397394
* fseek return with succeed and ftell return already set position
395+
* Check actual character read or written.
398396
*
399397
*/
400398
void test_fseek_ftell()
@@ -413,19 +411,6 @@ void test_fseek_ftell()
413411
ftell_ret = std::ftell(file);
414412
TEST_ASSERT_EQUAL(0, ftell_ret);
415413

416-
TestFile<FS>::resetFunctionCallHistory();
417-
fssek_ret = std::fseek(file, 0, SEEK_CUR);
418-
TEST_ASSERT_EQUAL(0, fssek_ret);
419-
420-
TestFile<FS>::resetFunctionCallHistory();
421-
fssek_ret = std::fseek(file, 0, SEEK_SET);
422-
TEST_ASSERT_EQUAL(0, fssek_ret);
423-
424-
TestFile<FS>::resetFunctionCallHistory();
425-
fssek_ret = std::fseek(file, 0, SEEK_END);
426-
TEST_ASSERT_TRUE(TestFile<FS>::functionCalled(TestFile<FS>::fnSeek));
427-
TEST_ASSERT_EQUAL(0, fssek_ret);
428-
429414
const char *str = "Hello world";
430415
const std::size_t size = std::strlen(str);
431416

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

444432
TestFile<FS>::resetFunctionCallHistory();
445-
fssek_ret = std::fseek(file, -5, SEEK_CUR);
433+
fssek_ret = std::fseek(file, -6, SEEK_CUR);
446434
TEST_ASSERT_EQUAL(0, fssek_ret);
447435
ftell_ret = std::ftell(file);
448436
TEST_ASSERT_EQUAL(0, ftell_ret);
437+
c = std::fgetc(file);
438+
TEST_ASSERT_TRUE(TestFile<FS>::functionCalled(TestFile<FS>::fnRead));
439+
TEST_ASSERT_EQUAL(c, str[0]);
449440

450441
TestFile<FS>::resetFunctionCallHistory();
451442
fssek_ret = std::fseek(file, 0, SEEK_END);
452-
TEST_ASSERT_TRUE(TestFile<FS>::functionCalled(TestFile<FS>::fnSeek));
453443
TEST_ASSERT_EQUAL(0, fssek_ret);
454444
ftell_ret = std::ftell(file);
455445
TEST_ASSERT_EQUAL(size, ftell_ret);
446+
c = std::fputc('!', file);
447+
TEST_ASSERT_TRUE(TestFile<FS>::functionCalled(TestFile<FS>::fnWrite));
448+
TEST_ASSERT_EQUAL(c, '!');
449+
TEST_ASSERT_EQUAL(fh.size(), size + 1);
456450

457451
std::fclose(file);
458452
}
459453

454+
/** Test ftruncate and fstat (st_size)
455+
*
456+
* Check we get EBADF for illegal handles
457+
*
458+
* Given already opened file is empty
459+
*
460+
* Check initial size is returned as 0
461+
* Call ftruncate with negative value - check our EINVAL is passed back
462+
* Call ftruncate with positive value to increase size - check no error return
463+
* Check fstat st_size now reads back as the value we set.
464+
* Call ftruncate with smaller positive value to decrease size - check no error return
465+
* Check fstat st_size now reads back as the value we set.
466+
*/
467+
void test_ftruncate_fstat()
468+
{
469+
int fildes;
470+
int ret;
471+
struct stat st;
472+
const uint32_t FS = 128;
473+
TestFile<FS> fh;
474+
475+
ret = ftruncate(12345678, 24);
476+
TEST_ASSERT_EQUAL(-1, ret);
477+
TEST_ASSERT_EQUAL(EBADF, errno);
478+
479+
ret = fstat(12345678, &st);
480+
TEST_ASSERT_EQUAL(-1, ret);
481+
TEST_ASSERT_EQUAL(EBADF, errno);
482+
483+
fildes = bind_to_fd(&fh);
484+
TEST_ASSERT_TRUE(fildes >= 0);
485+
486+
ret = fstat(fildes, &st);
487+
TEST_ASSERT_EQUAL(0, ret);
488+
TEST_ASSERT_EQUAL(0, st.st_size);
489+
490+
TestFile<FS>::resetFunctionCallHistory();
491+
ret = ftruncate(fildes, -3);
492+
TEST_ASSERT_TRUE(TestFile<FS>::functionCalled(TestFile<FS>::fnTruncate));
493+
TEST_ASSERT_EQUAL(-1, ret);
494+
TEST_ASSERT_EQUAL(EINVAL, errno);
495+
496+
TestFile<FS>::resetFunctionCallHistory();
497+
ret = ftruncate(fildes, 24);
498+
TEST_ASSERT_TRUE(TestFile<FS>::functionCalled(TestFile<FS>::fnTruncate));
499+
TEST_ASSERT_EQUAL(0, ret);
500+
501+
ret = fstat(fildes, &st);
502+
TEST_ASSERT_EQUAL(0, ret);
503+
TEST_ASSERT_EQUAL(24, st.st_size);
504+
505+
ret = ftruncate(fildes, 12);
506+
TEST_ASSERT_TRUE(TestFile<FS>::functionCalled(TestFile<FS>::fnTruncate));
507+
TEST_ASSERT_EQUAL(0, ret);
508+
509+
ret = fstat(fildes, &st);
510+
TEST_ASSERT_EQUAL(0, ret);
511+
TEST_ASSERT_EQUAL(12, st.st_size);
512+
513+
close(fildes);
514+
}
515+
460516
utest::v1::status_t test_setup(const size_t number_of_cases)
461517
{
462518
GREENTEA_SETUP(10, "default_auto");
@@ -469,7 +525,8 @@ Case cases[] = {
469525
Case("Test fputc/fgetc", test_fputc_fgetc),
470526
Case("Test fputs/fgets", test_fputs_fgets),
471527
Case("Test fprintf/fscanf", test_fprintf_fscanf),
472-
Case("Test fseek/ftell", test_fseek_ftell)
528+
Case("Test fseek/ftell", test_fseek_ftell),
529+
Case("Test ftruncate/fstat", test_ftruncate_fstat)
473530
};
474531

475532
utest::v1::Specification specification(test_setup, cases);

features/storage/filesystem/File.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,4 +110,10 @@ off_t File::size()
110110
return _fs->file_size(_file);
111111
}
112112

113+
int File::truncate(off_t length)
114+
{
115+
MBED_ASSERT(_fs);
116+
return _fs->file_truncate(_file, length);
117+
}
118+
113119
} // namespace mbed

features/storage/filesystem/File.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,18 @@ class File : public FileHandle {
126126
*/
127127
virtual off_t size();
128128

129+
/** Truncate or extend a file.
130+
*
131+
* The file's length is set to the specified value. The seek pointer is
132+
* not changed. If the file is extended, the extended area appears as if
133+
* it were zero-filled.
134+
*
135+
* @param length The requested new length for the file
136+
*
137+
* @return Zero on success, negative error code on failure
138+
*/
139+
virtual int truncate(off_t length);
140+
129141
private:
130142
FileSystem *_fs;
131143
fs_file_t _file;

features/storage/filesystem/FileSystem.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ off_t FileSystem::file_size(fs_file_t file)
8484
return size;
8585
}
8686

87+
int FileSystem::file_truncate(fs_file_t file, off_t length)
88+
{
89+
return -ENOSYS;
90+
}
91+
8792
int FileSystem::dir_open(fs_dir_t *dir, const char *path)
8893
{
8994
return -ENOSYS;

features/storage/filesystem/FileSystem.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,19 @@ class FileSystem : public FileSystemLike {
220220
*/
221221
virtual off_t file_size(fs_file_t file);
222222

223+
/** Truncate or extend a file.
224+
*
225+
* The file's length is set to the specified value. The seek pointer is
226+
* not changed. If the file is extended, the extended area appears as if
227+
* it were zero-filled.
228+
*
229+
* @param file File handle
230+
* @param length The requested new length for the file
231+
*
232+
* @return Zero on success, negative error code on failure
233+
*/
234+
virtual int file_truncate(fs_file_t file, off_t length);
235+
223236
/** Open a directory on the filesystem
224237
*
225238
* @param dir Destination for the handle to the directory

features/storage/filesystem/fat/FATFileSystem.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,37 @@ off_t FATFileSystem::file_size(fs_file_t file)
720720
return res;
721721
}
722722

723+
int FATFileSystem::file_truncate(fs_file_t file, off_t length)
724+
{
725+
FIL *fh = static_cast<FIL*>(file);
726+
727+
lock();
728+
// save current position
729+
FSIZE_t oldoff = f_tell(fh);
730+
731+
// seek to new file size and truncate
732+
FRESULT res = f_lseek(fh, length);
733+
if (res) {
734+
unlock();
735+
return fat_error_remap(res);
736+
}
737+
738+
res = f_truncate(fh);
739+
if (res) {
740+
unlock();
741+
return fat_error_remap(res);
742+
}
743+
744+
// restore old position
745+
res = f_lseek(fh, oldoff);
746+
if (res) {
747+
unlock();
748+
return fat_error_remap(res);
749+
}
750+
751+
return 0;
752+
}
753+
723754

724755
////// Dir operations //////
725756
int FATFileSystem::dir_open(fs_dir_t *dir, const char *path)

features/storage/filesystem/fat/FATFileSystem.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,19 @@ class FATFileSystem : public mbed::FileSystem {
217217
*/
218218
virtual off_t file_size(mbed::fs_file_t file);
219219

220+
/** Truncate or extend a file.
221+
*
222+
* The file's length is set to the specified value. The seek pointer is
223+
* not changed. If the file is extended, the extended area appears as if
224+
* it were zero-filled.
225+
*
226+
* @param file File handle
227+
* @param length The requested new length for the file
228+
*
229+
* @return Zero on success, negative error code on failure
230+
*/
231+
virtual int file_truncate(mbed::fs_file_t file, off_t length);
232+
220233
/** Open a directory on the filesystem
221234
*
222235
* @param dir Destination for the handle to the directory

features/storage/filesystem/littlefs/LittleFileSystem.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,17 @@ off_t LittleFileSystem::file_size(fs_file_t file)
474474
return lfs_toerror(res);
475475
}
476476

477+
int LittleFileSystem::file_truncate(fs_file_t file, off_t length)
478+
{
479+
lfs_file_t *f = (lfs_file_t *)file;
480+
_mutex.lock();
481+
LFS_INFO("file_truncate(%p)", file);
482+
int err = lfs_file_truncate(&_lfs, f, length);
483+
LFS_INFO("file_truncate -> %d", lfs_toerror(err));
484+
_mutex.unlock();
485+
return lfs_toerror(err);
486+
}
487+
477488

478489
////// Dir operations //////
479490
int LittleFileSystem::dir_open(fs_dir_t *dir, const char *path)

features/storage/filesystem/littlefs/LittleFileSystem.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,19 @@ class LittleFileSystem : public mbed::FileSystem {
220220
*/
221221
virtual off_t file_size(mbed::fs_file_t file);
222222

223+
/** Truncate or extend a file.
224+
*
225+
* The file's length is set to the specified value. The seek pointer is
226+
* not changed. If the file is extended, the extended area appears as if
227+
* it were zero-filled.
228+
*
229+
* @param file File handle
230+
* @param length The requested new length for the file
231+
*
232+
* @return Zero on success, negative error code on failure
233+
*/
234+
virtual int file_truncate(mbed::fs_file_t file, off_t length);
235+
223236
/** Open a directory on the filesystem
224237
*
225238
* @param dir Destination for the handle to the directory

0 commit comments

Comments
 (0)