This is the mail archive of the
ecos-patches@sourceware.org
mailing list for the eCos project.
ftruncate function for eCos FAT filesystem
- From: Gratian Crisan <nelu at iv dot ro>
- To: ecos-patches at ecos dot sourceware dot org
- Date: Mon, 05 Sep 2005 15:21:03 +0300
- Subject: ftruncate function for eCos FAT filesystem
Hi,
attached is a patch for a ftruncate function implementation for the eCos
FAT filesystem. If it's useful to anybody please feel free to use it.
regards,
Nelu
Index: packages/fs/fat/current/src/fatfs.c
===================================================================
RCS file: /cvs/ecos/ecos/packages/fs/fat/current/src/fatfs.c,v
retrieving revision 1.4
diff -u -p -r1.4 fatfs.c
--- packages/fs/fat/current/src/fatfs.c 11 Nov 2004 19:33:30 -0000 1.4
+++ packages/fs/fat/current/src/fatfs.c 5 Sep 2005 12:13:07 -0000
@@ -1290,6 +1290,43 @@ fatfs_fo_write(struct CYG_FILE_TAG *fp,
}
// -------------------------------------------------------------------------
+// fatfs_fo_ftruncate()
+// The ftruncate function changes the size of filename to length. If length
+// is shorter than the previous length, data at the end will be lost.
+
+int
+fatfs_fo_ftruncate(struct CYG_FILE_TAG *fp, off_t length)
+{
+ fatfs_disk_t *disk = (fatfs_disk_t *) fp->f_mte->data;
+ fatfs_fd_t *fd = (fatfs_fd_t *) fp->f_data;
+ fatfs_dir_entry_t *file = (fatfs_dir_entry_t *) &fd->node->dentry;
+ int err;
+
+ if (length == file->size) // if the requested file size is equal with the current file size
+ return ENOERR; // return
+
+ if (length < 0)
+ return EINVAL;
+
+ if (length == 0)
+ return fatfs_trunc_file(disk, file); // truncate file to zero length
+
+ if (length < file->size) {
+ err = fatfs_shorten_file(disk, file, &fd->pos, length);
+ } else {
+ err = fatfs_extend_file(disk, file, &fd->pos, length);
+ }
+
+ if (ENOERR == err) {
+ file->size = length;
+ file->mtime =
+ file->atime = cyg_timestamp();
+ }
+
+ return err;
+}
+
+// -------------------------------------------------------------------------
// fatfs_fo_lseek()
// Seek to a new file position.
Index: packages/fs/fat/current/src/fatfs.h
===================================================================
RCS file: /cvs/ecos/ecos/packages/fs/fat/current/src/fatfs.h,v
retrieving revision 1.3
diff -u -p -r1.3 fatfs.h
--- packages/fs/fat/current/src/fatfs.h 22 Oct 2004 14:10:23 -0000 1.3
+++ packages/fs/fat/current/src/fatfs.h 5 Sep 2005 12:13:07 -0000
@@ -233,6 +233,16 @@ int fatfs_create_dir(fatfs_disk_t
int fatfs_trunc_file(fatfs_disk_t *disk, fatfs_dir_entry_t *file);
+int fatfs_shorten_file(fatfs_disk_t *disk,
+ fatfs_dir_entry_t *file,
+ fatfs_data_pos_t *pos,
+ off_t length);
+
+int fatfs_extend_file(fatfs_disk_t *disk,
+ fatfs_dir_entry_t *file,
+ fatfs_data_pos_t *pos,
+ off_t length);
+
int fatfs_rename_file(fatfs_disk_t *disk,
fatfs_dir_entry_t *dir1,
fatfs_dir_entry_t *target,
Index: packages/fs/fat/current/src/fatfs_supp.c
===================================================================
RCS file: /cvs/ecos/ecos/packages/fs/fat/current/src/fatfs_supp.c,v
retrieving revision 1.6
diff -u -p -r1.6 fatfs_supp.c
--- packages/fs/fat/current/src/fatfs_supp.c 3 Aug 2005 20:40:48 -0000 1.6
+++ packages/fs/fat/current/src/fatfs_supp.c 5 Sep 2005 12:13:09 -0000
@@ -2341,6 +2341,154 @@ fatfs_trunc_file(fatfs_disk_t *disk, fat
}
// -------------------------------------------------------------------------
+// erase_cluster_section()
+// Erases a portion of a cluster with length 'length'
+// starting from offset 'pos'(fills with 0x00).
+
+static int
+erase_cluster_section(fatfs_disk_t *disk, cyg_uint32 cluster, cyg_uint32 pos, cyg_uint32 length)
+{
+ cyg_uint8 data[32];
+ int len;
+ int err;
+
+ if ((pos + length) > disk->cluster_size)
+ return EINVAL;
+
+ memset((void*)data, 0x00, 32);
+
+ while (length > 0) {
+ len = (length < 32) ? length : 32;
+ err = disk_cluster_write(disk, (void*)data, &len, cluster, pos);
+ if (err != ENOERR)
+ return err;
+
+ pos += len;
+ length -= len;
+ }
+
+ return ENOERR;
+}
+
+// -------------------------------------------------------------------------
+// fatfs_shorten_file()
+
+int
+fatfs_shorten_file(fatfs_disk_t *disk,
+ fatfs_dir_entry_t *file,
+ fatfs_data_pos_t *pos,
+ off_t length)
+{
+ cyg_uint32 next_c, tentry;
+ int err;
+
+ // if the file is shrinking,
+ // find the last cluster of the new file length (get_position_from_off),
+ // free all clusters from the current one to the end of the file (free_cluster_chain),
+ // and change the size of the coresponding fatfs node.
+
+ err = get_position_from_off(disk, file->cluster, length, pos, CO_NONE);
+ if (err != ENOERR)
+ return err;
+
+ // get next cluster in chain and free all clusters to the end of file
+ err = read_tentry(disk, pos->cluster, &tentry);
+ if (err != ENOERR)
+ return err;
+
+ switch (get_tentry_type(disk, tentry))
+ {
+ case TENTRY_LAST:
+ // Last cluster in chain no deallocation needed
+ return ENOERR;
+ case TENTRY_REGULAR:
+ // Get next cluster in chain
+ next_c = get_tentry_next_cluster(disk, tentry);
+ break;
+ default:
+ CYG_TRACE2(TCL, "!!! inconsistant FAT tentry=%x c=%d",
+ tentry, pos->cluster);
+ return EIO;
+ }
+
+ err = free_cluster_chain(disk, next_c);
+ if (err != ENOERR)
+ return err;
+
+ // Set the current cluster tentry to LAST
+ set_tentry_type(disk, &tentry, TENTRY_LAST);
+ return write_tentry(disk, pos->cluster, &tentry);
+}
+
+// -------------------------------------------------------------------------
+// fatfs_extend_file()
+
+int
+fatfs_extend_file(fatfs_disk_t *disk,
+ fatfs_dir_entry_t *file,
+ fatfs_data_pos_t *pos,
+ off_t length)
+{
+ // if the file is extending,
+ // allocate the appropriate number of clusters and link them into the file cluster chain
+ // (find_and_append_cluster).
+
+ off_t space_last_c = 0;
+ off_t space_needed = 0;
+ int err;
+ cyg_uint32 last_cluster;
+
+ // compute how much space is needed
+ space_needed = length - file->size;
+
+ // compute how much space there is available in the last file cluster
+ if ((space_last_c = file->size % disk->cluster_size) > 0)
+ space_last_c = disk->cluster_size - space_last_c;
+
+ // find the last cluster in the original file
+ err = get_position_from_off(disk, file->cluster, file->size, pos, CO_NONE);
+ if ((ENOERR != err) && (EEOF != err))
+ return err;
+ last_cluster = pos->cluster;
+
+ if (space_needed > space_last_c) {
+ cyg_uint32 new_cluster;
+ off_t new_c = 0;
+ cyg_uint32 cnt;
+
+ // erase the free part of the last cluster
+ if (space_last_c > 0)
+ erase_cluster_section(disk, last_cluster, disk->cluster_size - space_last_c, space_last_c);
+
+ // compute how many new clusters we need
+ new_c = (space_needed - space_last_c) / disk->cluster_size;
+
+ // account for an incomplete cluster at the end of file
+ new_c += ((space_needed - space_last_c) % disk->cluster_size) ? 1:0;
+
+ // extend the chain with new_c clusters
+ for (cnt=0; new_c > 0; new_c--, cnt++) {
+ err = find_and_append_cluster(disk, last_cluster, &new_cluster, CO_ERASE_NEW);
+ if (err != ENOERR) {
+ // adjust the file size to account for the allocated clusters
+ file->size += cnt*disk->cluster_size;
+ file->mtime =
+ file->atime = cyg_timestamp();
+ return err;
+ }
+
+ last_cluster = new_cluster;
+ }
+ } else {
+ // if we fit in the last cluster
+ // erase the requested part of the last cluster
+ erase_cluster_section(disk, last_cluster, disk->cluster_size - space_last_c, space_needed);
+ }
+
+ return ENOERR;
+}
+
+// -------------------------------------------------------------------------
// fatfs_rename_file()
// Renames a file.