diff options
Diffstat (limited to 'firmware/common/file.c')
-rw-r--r-- | firmware/common/file.c | 57 |
1 files changed, 33 insertions, 24 deletions
diff --git a/firmware/common/file.c b/firmware/common/file.c index 6444918f1b..1f93824dc8 100644 --- a/firmware/common/file.c +++ b/firmware/common/file.c | |||
@@ -54,7 +54,7 @@ static struct filestr_desc * get_filestr(int fildes) | |||
54 | return file; | 54 | return file; |
55 | 55 | ||
56 | DEBUGF("fildes %d: bad file number\n", fildes); | 56 | DEBUGF("fildes %d: bad file number\n", fildes); |
57 | errno = (file && file->stream.flags == FV_NONEXIST) ? ENXIO : EBADF; | 57 | errno = (file && (file->stream.flags & FD_NONEXIST)) ? ENXIO : EBADF; |
58 | return NULL; | 58 | return NULL; |
59 | } | 59 | } |
60 | 60 | ||
@@ -187,24 +187,28 @@ file_error: | |||
187 | return rc; | 187 | return rc; |
188 | } | 188 | } |
189 | 189 | ||
190 | /* callback for each file stream to make sure all data is in sync with new | 190 | /* Handle syncing all file's streams to the truncation */ |
191 | size */ | 191 | static void handle_truncate(struct filestr_desc * const file, file_size_t size) |
192 | void ftruncate_internal_callback(struct filestr_base *stream, | ||
193 | struct filestr_base *s) | ||
194 | { | 192 | { |
195 | struct filestr_desc *file = (struct filestr_desc *)s; | 193 | unsigned long filesectors = filesize_sectors(size); |
196 | file_size_t size = *file->sizep; | ||
197 | |||
198 | /* caches with data beyond new extents are invalid */ | ||
199 | unsigned long sector = file->stream.cachep->sector; | ||
200 | if (sector != INVALID_SECNUM && sector >= filesize_sectors(size)) | ||
201 | filestr_discard_cache(&file->stream); | ||
202 | 194 | ||
203 | /* keep all positions within bounds */ | 195 | struct filestr_base *s = NULL; |
204 | if (file->offset > size) | 196 | while ((s = fileobj_get_next_stream(&file->stream, s))) |
205 | file->offset = size; | 197 | { |
206 | 198 | /* caches with data beyond new extents are invalid */ | |
207 | (void)stream; | 199 | unsigned long sector = s->cachep->sector; |
200 | if (sector != INVALID_SECNUM && sector >= filesectors) | ||
201 | filestr_discard_cache(s); | ||
202 | |||
203 | /* files outside bounds must be rewound */ | ||
204 | if (fat_query_sectornum(&s->fatstr) > filesectors) | ||
205 | fat_seek_to_stream(&s->fatstr, &file->stream.fatstr); | ||
206 | |||
207 | /* clip file offset too if needed */ | ||
208 | struct filestr_desc *f = (struct filestr_desc *)s; | ||
209 | if (f->offset > size) | ||
210 | f->offset = size; | ||
211 | } | ||
208 | } | 212 | } |
209 | 213 | ||
210 | /* truncate the file to the specified length */ | 214 | /* truncate the file to the specified length */ |
@@ -246,13 +250,17 @@ static int ftruncate_internal(struct filestr_desc *file, file_size_t size, | |||
246 | rc2 = fat_truncate(&file->stream.fatstr); | 250 | rc2 = fat_truncate(&file->stream.fatstr); |
247 | if (rc2 < 0) | 251 | if (rc2 < 0) |
248 | FILE_ERROR(EIO, rc2 * 10 - 3); | 252 | FILE_ERROR(EIO, rc2 * 10 - 3); |
253 | |||
254 | /* never needs to be done this way again since any data beyond the | ||
255 | cached size is now gone */ | ||
256 | fileobj_change_flags(&file->stream, 0, FO_TRUNC); | ||
249 | } | 257 | } |
250 | /* else just change the cached file size */ | 258 | /* else just change the cached file size */ |
251 | 259 | ||
252 | if (truncsize < cursize) | 260 | if (truncsize < cursize) |
253 | { | 261 | { |
254 | *file->sizep = truncsize; | 262 | *file->sizep = truncsize; |
255 | fileop_ontruncate_internal(&file->stream); | 263 | handle_truncate(file, truncsize); |
256 | } | 264 | } |
257 | 265 | ||
258 | /* if truncation was partially successful, it effectively destroyed | 266 | /* if truncation was partially successful, it effectively destroyed |
@@ -299,10 +307,6 @@ static int fsync_internal(struct filestr_desc *file) | |||
299 | int rc2 = ftruncate_internal(file, size, rc == 0); | 307 | int rc2 = ftruncate_internal(file, size, rc == 0); |
300 | if (rc2 < 0) | 308 | if (rc2 < 0) |
301 | FILE_ERROR(ERRNO, rc2 * 10 - 2); | 309 | FILE_ERROR(ERRNO, rc2 * 10 - 2); |
302 | |||
303 | /* never needs to be done this way again since any data beyond the | ||
304 | cached size is now gone */ | ||
305 | fileobj_change_flags(&file->stream, 0, FO_TRUNC); | ||
306 | } | 310 | } |
307 | 311 | ||
308 | file_error:; | 312 | file_error:; |
@@ -327,8 +331,7 @@ static int close_internal(struct filestr_desc *file) | |||
327 | /* call only when holding WRITER lock (updates directory entries) */ | 331 | /* call only when holding WRITER lock (updates directory entries) */ |
328 | int rc; | 332 | int rc; |
329 | 333 | ||
330 | if ((file->stream.flags & FD_WRITE) && | 334 | if ((file->stream.flags & (FD_WRITE|FD_NONEXIST)) == FD_WRITE) |
331 | !(fileobj_get_flags(&file->stream) & FO_REMOVED)) | ||
332 | { | 335 | { |
333 | rc = fsync_internal(file); | 336 | rc = fsync_internal(file); |
334 | if (rc < 0) | 337 | if (rc < 0) |
@@ -786,6 +789,12 @@ int open_noiso_internal(const char *path, int oflag) | |||
786 | return open_internal_locked(path, oflag, FF_ANYTYPE | FF_NOISO); | 789 | return open_internal_locked(path, oflag, FF_ANYTYPE | FF_NOISO); |
787 | } | 790 | } |
788 | 791 | ||
792 | void force_close_writer_internal(struct filestr_base *stream) | ||
793 | { | ||
794 | /* only we do writers so we know this is our guy */ | ||
795 | close_internal((struct filestr_desc *)stream); | ||
796 | } | ||
797 | |||
789 | 798 | ||
790 | /** POSIX **/ | 799 | /** POSIX **/ |
791 | 800 | ||