1 /* ----> DO NOT REMOVE THE FOLLOWING NOTICE <----
2 *
3 * Copyright (c) 2014-2015 Datalight, Inc.
4 * All Rights Reserved Worldwide.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; use version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but "AS-IS," WITHOUT ANY WARRANTY; without even the implied warranty
12 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 /* Businesses and individuals that for commercial or other reasons cannot
21 * comply with the terms of the GPLv2 license may obtain a commercial license
22 * before incorporating Reliance Edge into proprietary software for
23 * distribution in any form. Visit http://www.datalight.com/reliance-edge for
24 * more information.
25 */
26
27 /** @file
28 * @brief Implements the entry-points to the core file system.
29 */
30 #include <redfs.h>
31 #include <redcoreapi.h>
32 #include <redcore.h>
33
34
35 /* Minimum number of blocks needed for metadata on any volume: the master
36 * block (1), the two metaroots (2), and one doubly-allocated inode (2),
37 * resulting in 1 + 2 + 2 = 5.
38 */
39 #define MINIMUM_METADATA_BLOCKS ( 5U )
40
41
42 #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX == 1 )
43 static REDSTATUS CoreCreate( uint32_t ulPInode,
44 const char * pszName,
45 bool fDir,
46 uint32_t * pulInode );
47 #endif
48 #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX == 1 ) && ( REDCONF_API_POSIX_LINK == 1 )
49 static REDSTATUS CoreLink( uint32_t ulPInode,
50 const char * pszName,
51 uint32_t ulInode );
52 #endif
53 #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX == 1 ) && ( ( REDCONF_API_POSIX_UNLINK == 1 ) || ( REDCONF_API_POSIX_RMDIR == 1 ) )
54 static REDSTATUS CoreUnlink( uint32_t ulPInode,
55 const char * pszName );
56 #endif
57 #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX == 1 ) && ( REDCONF_API_POSIX_RENAME == 1 )
58 static REDSTATUS CoreRename( uint32_t ulSrcPInode,
59 const char * pszSrcName,
60 uint32_t ulDstPInode,
61 const char * pszDstName );
62 #endif
63 #if REDCONF_READ_ONLY == 0
64 static REDSTATUS CoreFileWrite( uint32_t ulInode,
65 uint64_t ullStart,
66 uint32_t * pulLen,
67 const void * pBuffer );
68 #endif
69 #if TRUNCATE_SUPPORTED
70 static REDSTATUS CoreFileTruncate( uint32_t ulInode,
71 uint64_t ullSize );
72 #endif
73
74
75 VOLUME gaRedVolume[ REDCONF_VOLUME_COUNT ];
76 static COREVOLUME gaCoreVol[ REDCONF_VOLUME_COUNT ];
77
78 const VOLCONF * CONST_IF_ONE_VOLUME gpRedVolConf = &gaRedVolConf[ 0U ];
79 VOLUME * CONST_IF_ONE_VOLUME gpRedVolume = &gaRedVolume[ 0U ];
80 COREVOLUME * CONST_IF_ONE_VOLUME gpRedCoreVol = &gaCoreVol[ 0U ];
81 METAROOT * gpRedMR = &gaCoreVol[ 0U ].aMR[ 0U ];
82
83 CONST_IF_ONE_VOLUME uint8_t gbRedVolNum = 0;
84
85
86 /** @brief Initialize the Reliance Edge file system driver.
87 *
88 * Prepares the Reliance Edge file system driver to be used. Must be the first
89 * Reliance Edge function to be invoked: no volumes can be mounted until the
90 * driver has been initialized.
91 *
92 * If this function is called when the Reliance Edge driver is already
93 * initialized, the behavior is undefined.
94 *
95 * @return A negated ::REDSTATUS code indicating the operation result.
96 *
97 * @retval 0 Operation was successful.
98 */
RedCoreInit(void)99 REDSTATUS RedCoreInit( void )
100 {
101 REDSTATUS ret = 0;
102 uint8_t bVolNum;
103
104 #if REDCONF_OUTPUT == 1
105 static uint8_t bSignedOn = 0U; /* Whether the sign on has been printed. */
106
107 if( bSignedOn == 0U )
108 {
109 RedSignOn();
110 bSignedOn = 1U;
111 }
112 #else
113
114 /* Call RedSignOn() even when output is disabled, to force the copyright
115 * text to be referenced and pulled into the program data.
116 */
117 RedSignOn();
118 #endif
119
120 RedMemSet( gaRedVolume, 0U, sizeof( gaRedVolume ) );
121 RedMemSet( gaCoreVol, 0U, sizeof( gaCoreVol ) );
122
123 RedBufferInit();
124
125 for( bVolNum = 0U; bVolNum < REDCONF_VOLUME_COUNT; bVolNum++ )
126 {
127 VOLUME * pVol = &gaRedVolume[ bVolNum ];
128 COREVOLUME * pCoreVol = &gaCoreVol[ bVolNum ];
129 const VOLCONF * pVolConf = &gaRedVolConf[ bVolNum ];
130
131 if( ( pVolConf->ulSectorSize < SECTOR_SIZE_MIN ) ||
132 ( ( REDCONF_BLOCK_SIZE % pVolConf->ulSectorSize ) != 0U ) ||
133 ( pVolConf->ulInodeCount == 0U ) )
134 {
135 ret = -RED_EINVAL;
136 }
137
138 #if REDCONF_API_POSIX == 1
139 else if( pVolConf->pszPathPrefix == NULL )
140 {
141 ret = -RED_EINVAL;
142 }
143 else
144 {
145 #if REDCONF_VOLUME_COUNT > 1U
146 uint8_t bCmpVol;
147
148 /* Ensure there are no duplicate path prefixes. Check against all
149 * previous volumes, which are already verified.
150 */
151 for( bCmpVol = 0U; bCmpVol < bVolNum; bCmpVol++ )
152 {
153 const char * pszCmpPathPrefix = gaRedVolConf[ bCmpVol ].pszPathPrefix;
154
155 if( RedStrCmp( pVolConf->pszPathPrefix, pszCmpPathPrefix ) == 0 )
156 {
157 ret = -RED_EINVAL;
158 break;
159 }
160 }
161 #endif /* if REDCONF_VOLUME_COUNT > 1U */
162 }
163 #endif /* if REDCONF_API_POSIX == 1 */
164
165 if( ret == 0 )
166 {
167 pVol->bBlockSectorShift = 0U;
168
169 while( ( pVolConf->ulSectorSize << pVol->bBlockSectorShift ) < REDCONF_BLOCK_SIZE )
170 {
171 pVol->bBlockSectorShift++;
172 }
173
174 /* This should always be true since the block size is confirmed to
175 * be a power of two (checked at compile time) and above we ensured
176 * that (REDCONF_BLOCK_SIZE % pVolConf->ulSectorSize) == 0.
177 */
178 REDASSERT( ( pVolConf->ulSectorSize << pVol->bBlockSectorShift ) == REDCONF_BLOCK_SIZE );
179
180 pVol->ulBlockCount = ( uint32_t ) ( pVolConf->ullSectorCount >> pVol->bBlockSectorShift );
181
182 if( pVol->ulBlockCount < MINIMUM_METADATA_BLOCKS )
183 {
184 ret = -RED_EINVAL;
185 }
186 else
187 {
188 #if REDCONF_READ_ONLY == 0
189 pVol->ulTransMask = REDCONF_TRANSACT_DEFAULT;
190 #endif
191
192 pVol->ullMaxInodeSize = INODE_SIZE_MAX;
193
194 /* To understand the following code, note that the fixed-
195 * location metadata is located at the start of the disk, in
196 * the following order:
197 *
198 * - Master block (1 block)
199 * - Metaroots (2 blocks)
200 * - External imap blocks (variable * 2 blocks)
201 * - Inode blocks (pVolConf->ulInodeCount * 2 blocks)
202 */
203
204 /* The imap needs bits for all inode and allocable blocks. If
205 * that bitmap will fit into the metaroot, the inline imap is
206 * used and there are no imap nodes on disk. The minus 3 is
207 * there since the imap does not include bits for the master
208 * block or metaroots.
209 */
210 pCoreVol->fImapInline = ( pVol->ulBlockCount - 3U ) <= METAROOT_ENTRIES;
211
212 if( pCoreVol->fImapInline )
213 {
214 #if REDCONF_IMAP_INLINE == 1
215 pCoreVol->ulInodeTableStartBN = 3U;
216 #else
217 ret = -RED_EINVAL;
218 #endif
219 }
220 else
221 {
222 #if REDCONF_IMAP_EXTERNAL == 1
223 pCoreVol->ulImapStartBN = 3U;
224
225 /* The imap does not include bits for itself, so add two to
226 * the number of imap entries for the two blocks of each
227 * imap node. This allows us to divide up the remaining
228 * space, making sure to round up so all data blocks are
229 * covered.
230 */
231 pCoreVol->ulImapNodeCount =
232 ( ( pVol->ulBlockCount - 3U ) + ( ( IMAPNODE_ENTRIES + 2U ) - 1U ) ) / ( IMAPNODE_ENTRIES + 2U );
233
234 pCoreVol->ulInodeTableStartBN = pCoreVol->ulImapStartBN + ( pCoreVol->ulImapNodeCount * 2U );
235 #else
236 ret = -RED_EINVAL;
237 #endif
238 }
239 }
240 }
241
242 if( ret == 0 )
243 {
244 pCoreVol->ulFirstAllocableBN = pCoreVol->ulInodeTableStartBN + ( pVolConf->ulInodeCount * 2U );
245
246 if( pCoreVol->ulFirstAllocableBN > pVol->ulBlockCount )
247 {
248 /* We can get here if there is not enough space for the number
249 * of configured inodes.
250 */
251 ret = -RED_EINVAL;
252 }
253 else
254 {
255 pVol->ulBlocksAllocable = pVol->ulBlockCount - pCoreVol->ulFirstAllocableBN;
256 }
257 }
258
259 if( ret != 0 )
260 {
261 break;
262 }
263 }
264
265 /* Make sure the configured endianness is correct.
266 */
267 if( ret == 0 )
268 {
269 uint16_t uValue = 0xFF00U;
270 uint8_t abBytes[ 2U ];
271
272 RedMemCpy( abBytes, &uValue, sizeof( abBytes ) );
273
274 #if REDCONF_ENDIAN_BIG == 1
275 if( abBytes[ 0U ] != 0xFFU )
276 #else
277 if( abBytes[ 0U ] != 0x00U )
278 #endif
279 {
280 ret = -RED_EINVAL;
281 }
282 }
283
284 if( ret == 0 )
285 {
286 ret = RedOsClockInit();
287
288 #if REDCONF_TASK_COUNT > 1U
289 if( ret == 0 )
290 {
291 ret = RedOsMutexInit();
292
293 if( ret != 0 )
294 {
295 ( void ) RedOsClockUninit();
296 }
297 }
298 #endif
299 }
300
301 return ret;
302 }
303
304
305 /** @brief Uninitialize the Reliance Edge file system driver.
306 *
307 * Tears down the Reliance Edge file system driver. Cannot be used until all
308 * Reliance Edge volumes are unmounted. A subsequent call to RedCoreInit()
309 * will initialize the driver again.
310 *
311 * The behavior of calling this function when the core is already uninitialized
312 * is undefined.
313 *
314 * @return A negated ::REDSTATUS code indicating the operation result.
315 *
316 * @retval 0 Operation was successful.
317 * @retval -RED_EBUSY At least one volume is still mounted.
318 */
RedCoreUninit(void)319 REDSTATUS RedCoreUninit( void )
320 {
321 REDSTATUS ret;
322
323 #if REDCONF_TASK_COUNT > 1U
324 ret = RedOsMutexUninit();
325
326 if( ret == 0 )
327 #endif
328 {
329 ret = RedOsClockUninit();
330 }
331
332 return ret;
333 }
334
335
336 /** @brief Set the current volume.
337 *
338 * All core APIs operate on the current volume. This call must precede all
339 * core accesses.
340 *
341 * @param bVolNum The volume number to access.
342 *
343 * @return A negated ::REDSTATUS code indicating the operation result.
344 *
345 * @retval 0 Operation was successful.
346 * @retval -RED_EINVAL @p bVolNum is an invalid volume number.
347 */
RedCoreVolSetCurrent(uint8_t bVolNum)348 REDSTATUS RedCoreVolSetCurrent( uint8_t bVolNum )
349 {
350 REDSTATUS ret;
351
352 if( bVolNum >= REDCONF_VOLUME_COUNT )
353 {
354 ret = -RED_EINVAL;
355 }
356 else
357 {
358 #if REDCONF_VOLUME_COUNT > 1U
359 gbRedVolNum = bVolNum;
360 gpRedVolConf = &gaRedVolConf[ bVolNum ];
361 gpRedVolume = &gaRedVolume[ bVolNum ];
362 gpRedCoreVol = &gaCoreVol[ bVolNum ];
363 gpRedMR = &gpRedCoreVol->aMR[ gpRedCoreVol->bCurMR ];
364 #endif
365
366 ret = 0;
367 }
368
369 return ret;
370 }
371
372
373 #if FORMAT_SUPPORTED
374
375 /** @brief Format a file system volume.
376 *
377 * Uses the statically defined volume configuration. After calling this
378 * function, the volume needs to be mounted -- see RedCoreVolMount().
379 *
380 * An error is returned if the volume is mounted.
381 *
382 * @return A negated ::REDSTATUS code indicating the operation result.
383 *
384 * @retval 0 Operation was successful.
385 * @retval -RED_EBUSY Volume is mounted.
386 * @retval -RED_EIO A disk I/O error occurred.
387 */
RedCoreVolFormat(void)388 REDSTATUS RedCoreVolFormat( void )
389 {
390 return RedVolFormat();
391 }
392 #endif /* FORMAT_SUPPORTED */
393
394
395 /** @brief Mount a file system volume.
396 *
397 * Prepares the file system volume to be accessed. Mount will fail if the
398 * volume has never been formatted, or if the on-disk format is inconsistent
399 * with the compile-time configuration.
400 *
401 * If the volume is already mounted, the behavior is undefined.
402 *
403 * @return A negated ::REDSTATUS code indicating the operation result.
404 *
405 * @retval 0 Operation was successful.
406 * @retval -RED_EIO Volume not formatted, improperly formatted, or corrupt.
407 */
RedCoreVolMount(void)408 REDSTATUS RedCoreVolMount( void )
409 {
410 return RedVolMount();
411 }
412
413
414 /** @brief Unmount a file system volume.
415 *
416 * This function discards the in-memory state for the file system and marks it
417 * as unmounted. Subsequent attempts to access the volume will fail until the
418 * volume is mounted again.
419 *
420 * If unmount automatic transaction points are enabled, this function will
421 * commit a transaction point prior to unmounting. If unmount automatic
422 * transaction points are disabled, this function will unmount without
423 * transacting, effectively discarding the working state.
424 *
425 * If the volume is already unmounted, the behavior is undefined.
426 *
427 * @return A negated ::REDSTATUS code indicating the operation result.
428 *
429 * @retval 0 Operation was successful.
430 * @retval -RED_EIO I/O error during unmount automatic transaction point.
431 */
RedCoreVolUnmount(void)432 REDSTATUS RedCoreVolUnmount( void )
433 {
434 REDSTATUS ret = 0;
435
436 #if REDCONF_READ_ONLY == 0
437 if( !gpRedVolume->fReadOnly && ( ( gpRedVolume->ulTransMask & RED_TRANSACT_UMOUNT ) != 0U ) )
438 {
439 ret = RedVolTransact();
440 }
441 #endif
442
443 if( ret == 0 )
444 {
445 ret = RedBufferDiscardRange( 0U, gpRedVolume->ulBlockCount );
446 }
447
448 if( ret == 0 )
449 {
450 ret = RedOsBDevClose( gbRedVolNum );
451 }
452
453 if( ret == 0 )
454 {
455 gpRedVolume->fMounted = false;
456 }
457
458 return ret;
459 }
460
461
462 #if REDCONF_READ_ONLY == 0
463
464 /** @brief Commit a transaction point.
465 *
466 * Reliance Edge is a transactional file system. All modifications, of both
467 * metadata and filedata, are initially working state. A transaction point
468 * is a process whereby the working state atomically becomes the committed
469 * state, replacing the previous committed state. Whenever Reliance Edge is
470 * mounted, including after power loss, the state of the file system after
471 * mount is the most recent committed state. Nothing from the committed
472 * state is ever missing, and nothing from the working state is ever included.
473 *
474 * @return A negated ::REDSTATUS code indicating the operation result.
475 *
476 * @retval 0 Operation was successful.
477 * @retval -RED_EINVAL The volume is not mounted.
478 * @retval -RED_EIO A disk I/O error occurred.
479 * @retval -RED_EROFS The file system volume is read-only.
480 */
RedCoreVolTransact(void)481 REDSTATUS RedCoreVolTransact( void )
482 {
483 REDSTATUS ret;
484
485 if( !gpRedVolume->fMounted )
486 {
487 ret = -RED_EINVAL;
488 }
489 else if( gpRedVolume->fReadOnly )
490 {
491 ret = -RED_EROFS;
492 }
493 else
494 {
495 ret = RedVolTransact();
496 }
497
498 return ret;
499 }
500 #endif /* REDCONF_READ_ONLY == 0 */
501
502
503 #if REDCONF_API_POSIX == 1
504
505 /** @brief Query file system status information.
506 *
507 * @param pStatFS The buffer to populate with volume information.
508 *
509 * @return A negated ::REDSTATUS code indicating the operation result.
510 *
511 * @retval -RED_EINVAL Volume is not mounted; or @p pStatFS is `NULL`.
512 */
RedCoreVolStat(REDSTATFS * pStatFS)513 REDSTATUS RedCoreVolStat( REDSTATFS * pStatFS )
514 {
515 REDSTATUS ret;
516
517 if( ( pStatFS == NULL ) || ( !gpRedVolume->fMounted ) )
518 {
519 ret = -RED_EINVAL;
520 }
521 else
522 {
523 RedMemSet( pStatFS, 0U, sizeof( *pStatFS ) );
524
525 pStatFS->f_bsize = REDCONF_BLOCK_SIZE;
526 pStatFS->f_frsize = REDCONF_BLOCK_SIZE;
527 pStatFS->f_blocks = gpRedVolume->ulBlockCount;
528 #if RESERVED_BLOCKS > 0U
529 pStatFS->f_bfree = ( gpRedMR->ulFreeBlocks > RESERVED_BLOCKS ) ? ( gpRedMR->ulFreeBlocks - RESERVED_BLOCKS ) : 0U;
530 #else
531 pStatFS->f_bfree = gpRedMR->ulFreeBlocks;
532 #endif
533 pStatFS->f_bavail = pStatFS->f_bfree;
534 pStatFS->f_files = gpRedVolConf->ulInodeCount;
535 pStatFS->f_ffree = gpRedMR->ulFreeInodes;
536 pStatFS->f_favail = gpRedMR->ulFreeInodes;
537
538 pStatFS->f_flag = RED_ST_NOSUID;
539 #if REDCONF_READ_ONLY == 0
540 if( gpRedVolume->fReadOnly )
541 #endif
542 {
543 pStatFS->f_flag |= RED_ST_RDONLY;
544 }
545
546 pStatFS->f_namemax = REDCONF_NAME_MAX;
547 pStatFS->f_maxfsize = INODE_SIZE_MAX;
548 pStatFS->f_dev = gbRedVolNum;
549
550 ret = 0;
551 }
552
553 return ret;
554 }
555 #endif /* REDCONF_API_POSIX == 1 */
556
557
558 #if ( REDCONF_READ_ONLY == 0 ) && ( ( REDCONF_API_POSIX == 1 ) || ( REDCONF_API_FSE_TRANSMASKSET == 1 ) )
559
560 /** @brief Update the transaction mask.
561 *
562 * The following events are available when using the FSE API:
563 *
564 * - #RED_TRANSACT_UMOUNT
565 * - #RED_TRANSACT_WRITE
566 * - #RED_TRANSACT_TRUNCATE
567 * - #RED_TRANSACT_VOLFULL
568 *
569 * The following events are available when using the POSIX-like API:
570 *
571 * - #RED_TRANSACT_UMOUNT
572 * - #RED_TRANSACT_CREAT
573 * - #RED_TRANSACT_UNLINK
574 * - #RED_TRANSACT_MKDIR
575 * - #RED_TRANSACT_RENAME
576 * - #RED_TRANSACT_LINK
577 * - #RED_TRANSACT_CLOSE
578 * - #RED_TRANSACT_WRITE
579 * - #RED_TRANSACT_FSYNC
580 * - #RED_TRANSACT_TRUNCATE
581 * - #RED_TRANSACT_VOLFULL
582 *
583 * The #RED_TRANSACT_MANUAL macro (by itself) may be used to disable all
584 * automatic transaction events. The #RED_TRANSACT_MASK macro is a bitmask of
585 * all transaction flags, excluding those representing excluded functionality.
586 *
587 * Attempting to enable events for excluded functionality will result in an
588 * error.
589 *
590 * @param ulEventMask A bitwise-OR'd mask of automatic transaction events to
591 * be set as the current transaction mode.
592 *
593 * @return A negated ::REDSTATUS code indicating the operation result.
594 *
595 * @retval 0 Operation was successful.
596 * @retval -RED_EINVAL The volume is not mounted; or @p ulEventMask contains
597 * invalid bits.
598 * @retval -RED_EROFS The file system volume is read-only.
599 */
RedCoreTransMaskSet(uint32_t ulEventMask)600 REDSTATUS RedCoreTransMaskSet( uint32_t ulEventMask )
601 {
602 REDSTATUS ret;
603
604 if( !gpRedVolume->fMounted || ( ( ulEventMask & RED_TRANSACT_MASK ) != ulEventMask ) )
605 {
606 ret = -RED_EINVAL;
607 }
608 else if( gpRedVolume->fReadOnly )
609 {
610 ret = -RED_EROFS;
611 }
612 else
613 {
614 gpRedVolume->ulTransMask = ulEventMask;
615 ret = 0;
616 }
617
618 return ret;
619 }
620 #endif /* if ( REDCONF_READ_ONLY == 0 ) && ( ( REDCONF_API_POSIX == 1 ) || ( REDCONF_API_FSE_TRANSMASKSET == 1 ) ) */
621
622
623 #if ( REDCONF_API_POSIX == 1 ) || ( REDCONF_API_FSE_TRANSMASKGET == 1 )
624
625 /** @brief Read the transaction mask.
626 *
627 * If the volume is read-only, the returned event mask is always zero.
628 *
629 * @param pulEventMask Populated with a bitwise-OR'd mask of automatic
630 * transaction events which represent the current
631 * transaction mode for the volume.
632 *
633 * @return A negated ::REDSTATUS code indicating the operation result.
634 *
635 * @retval 0 Operation was successful.
636 * @retval -RED_EINVAL The volume is not mounted; or @p pulEventMask is `NULL`.
637 */
RedCoreTransMaskGet(uint32_t * pulEventMask)638 REDSTATUS RedCoreTransMaskGet( uint32_t * pulEventMask )
639 {
640 REDSTATUS ret;
641
642 if( !gpRedVolume->fMounted || ( pulEventMask == NULL ) )
643 {
644 ret = -RED_EINVAL;
645 }
646 else
647 {
648 #if REDCONF_READ_ONLY == 1
649 *pulEventMask = 0U;
650 #else
651 *pulEventMask = gpRedVolume->ulTransMask;
652 #endif
653 ret = 0;
654 }
655
656 return ret;
657 }
658 #endif /* if ( REDCONF_API_POSIX == 1 ) || ( REDCONF_API_FSE_TRANSMASKGET == 1 ) */
659
660
661 #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX == 1 )
662
663 /** @brief Create a file or directory.
664 *
665 * @param ulPInode The inode number of the parent directory.
666 * @param pszName A null-terminated name for the new inode.
667 * @param fDir Whether to create a directory (true) or file (false).
668 * @param pulInode On successful return, populated with the inode number of the
669 * new file or directory.
670 *
671 * @return A negated ::REDSTATUS code indicating the operation result.
672 *
673 * @retval 0 Operation was successful.
674 * @retval -RED_EINVAL The volume is not mounted; or @p pszName is not
675 * a valid name; or @p pulInode is `NULL`.
676 * @retval -RED_EIO A disk I/O error occurred.
677 * @retval -RED_EROFS The file system volume is read-only.
678 * @retval -RED_ENOTDIR @p ulPInode is not a directory.
679 * @retval -RED_EBADF @p ulPInode is not a valid inode.
680 * @retval -RED_ENOSPC There is not enough space on the volume to
681 * createthe new directory entry; or the directory
682 * is full.
683 * @retval -RED_ENFILE No available inode slots.
684 * @retval -RED_ENAMETOOLONG @p pszName is too long.
685 * @retval -RED_EEXIST @p pszName already exists in @p ulPInode.
686 */
RedCoreCreate(uint32_t ulPInode,const char * pszName,bool fDir,uint32_t * pulInode)687 REDSTATUS RedCoreCreate( uint32_t ulPInode,
688 const char * pszName,
689 bool fDir,
690 uint32_t * pulInode )
691 {
692 REDSTATUS ret;
693
694 if( !gpRedVolume->fMounted )
695 {
696 ret = -RED_EINVAL;
697 }
698 else if( gpRedVolume->fReadOnly )
699 {
700 ret = -RED_EROFS;
701 }
702 else
703 {
704 ret = CoreCreate( ulPInode, pszName, fDir, pulInode );
705
706 if( ( ret == -RED_ENOSPC ) &&
707 ( ( gpRedVolume->ulTransMask & RED_TRANSACT_VOLFULL ) != 0U ) &&
708 ( gpRedCoreVol->ulAlmostFreeBlocks > 0U ) )
709 {
710 ret = RedVolTransact();
711
712 if( ret == 0 )
713 {
714 ret = CoreCreate( ulPInode, pszName, fDir, pulInode );
715 }
716 }
717
718 if( ret == 0 )
719 {
720 if( fDir && ( ( gpRedVolume->ulTransMask & RED_TRANSACT_MKDIR ) != 0U ) )
721 {
722 ret = RedVolTransact();
723 }
724 else if( !fDir && ( ( gpRedVolume->ulTransMask & RED_TRANSACT_CREAT ) != 0U ) )
725 {
726 ret = RedVolTransact();
727 }
728 else
729 {
730 /* No automatic transaction for this operation.
731 */
732 }
733 }
734 }
735
736 return ret;
737 }
738
739
740 /** @brief Create a file or directory.
741 *
742 * @param ulPInode The inode number of the parent directory.
743 * @param pszName A null-terminated name for the new inode.
744 * @param fDir Whether to create a directory (true) or file (false).
745 * @param pulInode On successful return, populated with the inode number of the
746 * new file or directory.
747 *
748 * @return A negated ::REDSTATUS code indicating the operation result.
749 *
750 * @retval 0 Operation was successful.
751 * @retval -RED_EIO A disk I/O error occurred.
752 * @retval -RED_EROFS The file system volume is read-only.
753 * @retval -RED_ENOTDIR @p ulPInode is not a directory.
754 * @retval -RED_EBADF @p ulPInode is not a valid inode.
755 * @retval -RED_ENOSPC There is not enough space on the volume to
756 * create the new directory entry; or the directory
757 * is full.
758 * @retval -RED_ENFILE No available inode slots.
759 * @retval -RED_ENAMETOOLONG @p pszName is too long.
760 * @retval -RED_EEXIST @p pszName already exists in @p ulPInode.
761 */
CoreCreate(uint32_t ulPInode,const char * pszName,bool fDir,uint32_t * pulInode)762 static REDSTATUS CoreCreate( uint32_t ulPInode,
763 const char * pszName,
764 bool fDir,
765 uint32_t * pulInode )
766 {
767 REDSTATUS ret;
768
769 if( pulInode == NULL )
770 {
771 ret = -RED_EINVAL;
772 }
773 else if( gpRedVolume->fReadOnly )
774 {
775 ret = -RED_EROFS;
776 }
777 else
778 {
779 CINODE pino;
780
781 pino.ulInode = ulPInode;
782 ret = RedInodeMount( &pino, FTYPE_DIR, false );
783
784 if( ret == 0 )
785 {
786 CINODE ino;
787
788 ino.ulInode = INODE_INVALID;
789 ret = RedInodeCreate( &ino, ulPInode, fDir ? RED_S_IFDIR : RED_S_IFREG );
790
791 if( ret == 0 )
792 {
793 ret = RedInodeBranch( &pino );
794
795 if( ret == 0 )
796 {
797 ret = RedDirEntryCreate( &pino, pszName, ino.ulInode );
798 }
799
800 if( ret == 0 )
801 {
802 *pulInode = ino.ulInode;
803 }
804 else
805 {
806 REDSTATUS ret2;
807
808 ret2 = RedInodeFree( &ino );
809 CRITICAL_ASSERT( ret2 == 0 );
810 }
811
812 RedInodePut( &ino, 0U );
813 }
814
815 RedInodePut( &pino, ( ret == 0 ) ? ( uint8_t ) ( IPUT_UPDATE_MTIME | IPUT_UPDATE_CTIME ) : 0U );
816 }
817 }
818
819 return ret;
820 }
821 #endif /* (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX == 1) */
822
823
824 #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX == 1 ) && ( REDCONF_API_POSIX_LINK == 1 )
825
826 /** @brief Create a hard link.
827 *
828 * This creates an additional name (link) for @p ulInode. The new name refers
829 * to the same file with the same contents. If a name is deleted, but the
830 * underlying file has other names, the file continues to exist. The link
831 * count (accessible via RedCoreStat()) indicates the number of names that a
832 * file has. All of a file's names are on equal footing: there is nothing
833 * special about the original name.
834 *
835 * If @p ulInode names a directory, the operation will fail.
836 *
837 * @param ulPInode The inode number of the parent directory.
838 * @param pszName The null-terminated name for the new link.
839 * @param ulInode The inode to create a hard link to.
840 *
841 * @return A negated ::REDSTATUS code indicating the operation result.
842 *
843 * @retval 0 Operation was successful.
844 * @retval -RED_EBADF @p ulPInode is not a valid inode; or @p ulInode
845 * is not a valid inode.
846 * @retval -RED_EEXIST @p pszName resolves to an existing file.
847 * @retval -RED_EINVAL The volume is not mounted; or @p pszName is
848 * `NULL`.
849 * @retval -RED_EIO A disk I/O error occurred.
850 * @retval -RED_EMLINK Creating the link would exceed the maximum link
851 * count of @p ulInode.
852 * @retval -RED_ENAMETOOLONG Attempting to create a link with a name that
853 * exceeds the maximum name length.
854 * @retval -RED_ENOSPC There is insufficient free space to expand the
855 * directory that would contain the link.
856 * @retval -RED_ENOTDIR @p ulPInode is not a directory.
857 * @retval -RED_EPERM @p ulInode is a directory.
858 * @retval -RED_EROFS The requested link requires writing in a
859 * directory on a read-only file system.
860 */
RedCoreLink(uint32_t ulPInode,const char * pszName,uint32_t ulInode)861 REDSTATUS RedCoreLink( uint32_t ulPInode,
862 const char * pszName,
863 uint32_t ulInode )
864 {
865 REDSTATUS ret;
866
867 if( !gpRedVolume->fMounted )
868 {
869 ret = -RED_EINVAL;
870 }
871 else if( gpRedVolume->fReadOnly )
872 {
873 ret = -RED_EROFS;
874 }
875 else
876 {
877 ret = CoreLink( ulPInode, pszName, ulInode );
878
879 if( ( ret == -RED_ENOSPC ) &&
880 ( ( gpRedVolume->ulTransMask & RED_TRANSACT_VOLFULL ) != 0U ) &&
881 ( gpRedCoreVol->ulAlmostFreeBlocks > 0U ) )
882 {
883 ret = RedVolTransact();
884
885 if( ret == 0 )
886 {
887 ret = CoreLink( ulPInode, pszName, ulInode );
888 }
889 }
890
891 if( ( ret == 0 ) && ( ( gpRedVolume->ulTransMask & RED_TRANSACT_LINK ) != 0U ) )
892 {
893 ret = RedVolTransact();
894 }
895 }
896
897 return ret;
898 }
899
900
901 /** @brief Create a hard link.
902 *
903 * @param ulPInode The inode number of the parent directory.
904 * @param pszName The null-terminated name for the new link.
905 * @param ulInode The inode to create a hard link to.
906 *
907 * @return A negated ::REDSTATUS code indicating the operation result.
908 *
909 * @retval 0 Operation was successful.
910 * @retval -RED_EBADF @p ulPInode is not a valid inode; or @p ulInode
911 * is not a valid inode.
912 * @retval -RED_EEXIST @p pszName resolves to an existing file.
913 * @retval -RED_EIO A disk I/O error occurred.
914 * @retval -RED_EMLINK Creating the link would exceed the maximum link
915 * count of @p ulInode.
916 * @retval -RED_ENAMETOOLONG Attempting to create a link with a name that
917 * exceeds the maximum name length.
918 * @retval -RED_ENOSPC There is insufficient free space to expand the
919 * directory that would contain the link.
920 * @retval -RED_ENOTDIR @p ulPInode is not a directory.
921 * @retval -RED_EPERM @p ulInode is a directory.
922 * @retval -RED_EROFS The requested link requires writing in a
923 * directory on a read-only file system.
924 */
CoreLink(uint32_t ulPInode,const char * pszName,uint32_t ulInode)925 static REDSTATUS CoreLink( uint32_t ulPInode,
926 const char * pszName,
927 uint32_t ulInode )
928 {
929 REDSTATUS ret;
930
931 if( gpRedVolume->fReadOnly )
932 {
933 ret = -RED_EROFS;
934 }
935 else
936 {
937 CINODE pino;
938
939 pino.ulInode = ulPInode;
940 ret = RedInodeMount( &pino, FTYPE_DIR, false );
941
942 if( ret == 0 )
943 {
944 CINODE ino;
945
946 ino.ulInode = ulInode;
947 ret = RedInodeMount( &ino, FTYPE_FILE, false );
948
949 /* POSIX specifies EPERM as the errno thrown when link() is given a
950 * directory. Switch the errno returned if EISDIR was the return
951 * value.
952 */
953 if( ret == -RED_EISDIR )
954 {
955 ret = -RED_EPERM;
956 }
957
958 if( ret == 0 )
959 {
960 if( ino.pInodeBuf->uNLink == UINT16_MAX )
961 {
962 ret = -RED_EMLINK;
963 }
964 else
965 {
966 ret = RedInodeBranch( &pino );
967 }
968
969 if( ret == 0 )
970 {
971 ret = RedInodeBranch( &ino );
972 }
973
974 if( ret == 0 )
975 {
976 ret = RedDirEntryCreate( &pino, pszName, ino.ulInode );
977 }
978
979 if( ret == 0 )
980 {
981 ino.pInodeBuf->uNLink++;
982 }
983
984 RedInodePut( &ino, ( ret == 0 ) ? IPUT_UPDATE_CTIME : 0U );
985 }
986
987 RedInodePut( &pino, ( ret == 0 ) ? ( uint8_t ) ( IPUT_UPDATE_MTIME | IPUT_UPDATE_CTIME ) : 0U );
988 }
989 }
990
991 return ret;
992 }
993 #endif /* (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX == 1) && (REDCONF_API_POSIX_LINK == 1) */
994
995
996 #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX == 1 ) && ( ( REDCONF_API_POSIX_UNLINK == 1 ) || ( REDCONF_API_POSIX_RMDIR == 1 ) )
997
998 /** @brief Delete a file or directory.
999 *
1000 * The given name is deleted and the link count of the corresponding inode is
1001 * decremented. If the link count falls to zero (no remaining hard links),
1002 * the inode will be deleted.
1003 *
1004 * If the path names a directory which is not empty, the unlink will fail.
1005 *
1006 * If the deletion frees data in the committed state, it will not return to
1007 * free space until after a transaction point. Similarly, if the inode was
1008 * part of the committed state, the inode slot will not be available until
1009 * after a transaction point.
1010 *
1011 * This function can fail when the disk is full. To fix this, transact and
1012 * try again: Reliance Edge guarantees that it is possible to delete at least
1013 * one file or directory after a transaction point. If disk full automatic
1014 * transactions are enabled, this will happen automatically.
1015 *
1016 * @param ulPInode The inode number of the parent directory.
1017 * @param pszName The null-terminated name of the file or directory to
1018 * delete.
1019 *
1020 * @return A negated ::REDSTATUS code indicating the operation result.
1021 *
1022 * @retval 0 Operation was successful.
1023 * @retval -RED_EBADF @p ulPInode is not a valid inode.
1024 * @retval -RED_EINVAL The volume is not mounted; or @p pszName is
1025 * `NULL`.
1026 * @retval -RED_EIO A disk I/O error occurred.
1027 * @retval -RED_ENAMETOOLONG @p pszName is too long.
1028 * @retval -RED_ENOENT @p pszName does not name an existing file or
1029 * directory.
1030 * @retval -RED_ENOTDIR @p ulPInode is not a directory.
1031 * @retval -RED_ENOSPC The file system does not have enough space to
1032 * modify the parent directory to perform the
1033 * deletion.
1034 * @retval -RED_ENOTEMPTY The inode referred to by @p pszName is a
1035 * directory which is not empty.
1036 */
RedCoreUnlink(uint32_t ulPInode,const char * pszName)1037 REDSTATUS RedCoreUnlink( uint32_t ulPInode,
1038 const char * pszName )
1039 {
1040 REDSTATUS ret;
1041
1042 if( !gpRedVolume->fMounted )
1043 {
1044 ret = -RED_EINVAL;
1045 }
1046 else if( gpRedVolume->fReadOnly )
1047 {
1048 ret = -RED_EROFS;
1049 }
1050 else
1051 {
1052 ret = CoreUnlink( ulPInode, pszName );
1053
1054 if( ( ret == -RED_ENOSPC ) &&
1055 ( ( gpRedVolume->ulTransMask & RED_TRANSACT_VOLFULL ) != 0U ) &&
1056 ( gpRedCoreVol->ulAlmostFreeBlocks > 0U ) )
1057 {
1058 ret = RedVolTransact();
1059
1060 if( ret == 0 )
1061 {
1062 ret = CoreUnlink( ulPInode, pszName );
1063 }
1064 }
1065
1066 if( ( ret == 0 ) && ( ( gpRedVolume->ulTransMask & RED_TRANSACT_UNLINK ) != 0U ) )
1067 {
1068 ret = RedVolTransact();
1069 }
1070 }
1071
1072 return ret;
1073 }
1074
1075
1076 /** @brief Delete a file or directory.
1077 *
1078 * @param ulPInode The inode number of the parent directory.
1079 * @param pszName The null-terminated name of the file or directory to
1080 * delete.
1081 *
1082 * @return A negated ::REDSTATUS code indicating the operation result.
1083 *
1084 * @retval 0 Operation was successful.
1085 * @retval -RED_EBADF @p ulPInode is not a valid inode.
1086 * @retval -RED_EIO A disk I/O error occurred.
1087 * @retval -RED_ENAMETOOLONG @p pszName is too long.
1088 * @retval -RED_ENOENT @p pszName does not name an existing file or
1089 * directory.
1090 * @retval -RED_ENOTDIR @p ulPInode is not a directory.
1091 * @retval -RED_ENOSPC The file system does not have enough space to
1092 * modify the parent directory to perform the
1093 * deletion.
1094 * @retval -RED_ENOTEMPTY The inode referred to by @p pszName is a
1095 * directory which is not empty.
1096 */
CoreUnlink(uint32_t ulPInode,const char * pszName)1097 static REDSTATUS CoreUnlink( uint32_t ulPInode,
1098 const char * pszName )
1099 {
1100 REDSTATUS ret;
1101
1102 if( gpRedVolume->fReadOnly )
1103 {
1104 ret = -RED_EROFS;
1105 }
1106 else
1107 {
1108 CINODE pino;
1109
1110 pino.ulInode = ulPInode;
1111 ret = RedInodeMount( &pino, FTYPE_DIR, false );
1112
1113 if( ret == 0 )
1114 {
1115 uint32_t ulDeleteIdx;
1116 uint32_t ulInode;
1117
1118 ret = RedDirEntryLookup( &pino, pszName, &ulDeleteIdx, &ulInode );
1119
1120 if( ret == 0 )
1121 {
1122 ret = RedInodeBranch( &pino );
1123 }
1124
1125 if( ret == 0 )
1126 {
1127 CINODE ino;
1128
1129 ino.ulInode = ulInode;
1130 ret = RedInodeMount( &ino, FTYPE_EITHER, false );
1131
1132 if( ret == 0 )
1133 {
1134 if( ino.fDirectory && ( ino.pInodeBuf->ullSize > 0U ) )
1135 {
1136 ret = -RED_ENOTEMPTY;
1137 }
1138 else
1139 {
1140 #if RESERVED_BLOCKS > 0U
1141 gpRedCoreVol->fUseReservedBlocks = true;
1142 #endif
1143
1144 ret = RedDirEntryDelete( &pino, ulDeleteIdx );
1145
1146 #if RESERVED_BLOCKS > 0U
1147 gpRedCoreVol->fUseReservedBlocks = false;
1148 #endif
1149
1150 if( ret == 0 )
1151 {
1152 /* If the inode is deleted, buffers are needed to
1153 * read all of the indirects and free the data
1154 * blocks. Before doing that, to reduce the
1155 * minimum number of buffers needed to complete the
1156 * unlink, release the parent directory inode
1157 * buffers which are no longer needed.
1158 */
1159 RedInodePutCoord( &pino );
1160
1161 ret = RedInodeLinkDec( &ino );
1162 CRITICAL_ASSERT( ret == 0 );
1163 }
1164 }
1165
1166 RedInodePut( &ino, ( ret == 0 ) ? IPUT_UPDATE_CTIME : 0U );
1167 }
1168 }
1169
1170 RedInodePut( &pino, ( ret == 0 ) ? ( uint8_t ) ( IPUT_UPDATE_MTIME | IPUT_UPDATE_CTIME ) : 0U );
1171 }
1172 }
1173
1174 return ret;
1175 }
1176 #endif /* (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX == 1) && ((REDCONF_API_POSIX_UNLINK == 1) || (REDCONF_API_POSIX_RMDIR == 1)) */
1177
1178
1179 #if REDCONF_API_POSIX == 1
1180
1181 /** @brief Look up the inode number of a file or directory.
1182 *
1183 * @param ulPInode The inode number of the parent directory.
1184 * @param pszName The null-terminated name of the file or directory to look
1185 * up.
1186 * @param pulInode On successful return, populated with the inode number named
1187 * by @p pszName.
1188 *
1189 * @return A negated ::REDSTATUS code indicating the operation result.
1190 *
1191 * @retval 0 Operation was successful.
1192 * @retval -RED_EBADF @p ulPInode is not a valid inode.
1193 * @retval -RED_EINVAL The volume is not mounted; @p pszName is `NULL`; or
1194 * @p pulInode is `NULL`.
1195 * @retval -RED_EIO A disk I/O error occurred.
1196 * @retval -RED_ENOENT @p pszName does not name an existing file or directory.
1197 * @retval -RED_ENOTDIR @p ulPInode is not a directory.
1198 */
RedCoreLookup(uint32_t ulPInode,const char * pszName,uint32_t * pulInode)1199 REDSTATUS RedCoreLookup( uint32_t ulPInode,
1200 const char * pszName,
1201 uint32_t * pulInode )
1202 {
1203 REDSTATUS ret;
1204
1205 if( ( pulInode == NULL ) || !gpRedVolume->fMounted )
1206 {
1207 ret = -RED_EINVAL;
1208 }
1209 else
1210 {
1211 CINODE ino;
1212
1213 ino.ulInode = ulPInode;
1214 ret = RedInodeMount( &ino, FTYPE_DIR, false );
1215
1216 if( ret == 0 )
1217 {
1218 ret = RedDirEntryLookup( &ino, pszName, NULL, pulInode );
1219
1220 RedInodePut( &ino, 0U );
1221 }
1222 }
1223
1224 return ret;
1225 }
1226 #endif /* REDCONF_API_POSIX == 1 */
1227
1228
1229 #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX == 1 ) && ( REDCONF_API_POSIX_RENAME == 1 )
1230
1231 /** @brief Rename a file or directory.
1232 *
1233 * If @p pszDstName names an existing file or directory, the behavior depends
1234 * on the configuration. If #REDCONF_RENAME_ATOMIC is false, and if the
1235 * destination name exists, this function always fails with -RED_EEXIST.
1236 *
1237 * If #REDCONF_RENAME_ATOMIC is true, and if the new name exists, then in one
1238 * atomic operation, the destination name is unlinked and the source name is
1239 * renamed to the destination name. Both names must be of the same type (both
1240 * files or both directories). As with RedCoreUnlink(), if the destination
1241 * name is a directory, it must be empty. The major exception to this
1242 * behavior is that if both names are links to the same inode, then the rename
1243 * does nothing and both names continue to exist.
1244 *
1245 * If the rename deletes the old destination, it may free data in the
1246 * committed state, which will not return to free space until after a
1247 * transaction point. Similarly, if the deleted inode was part of the
1248 * committed state, the inode slot will not be available until after a
1249 * transaction point.
1250 *
1251 * @param ulSrcPInode The inode number of the parent directory of the file or
1252 * directory to rename.
1253 * @param pszSrcName The name of the file or directory to rename.
1254 * @param ulDstPInode The new parent directory inode number of the file or
1255 * directory after the rename.
1256 * @param pszDstName The new name of the file or directory after the rename.
1257 *
1258 * @return A negated ::REDSTATUS code indicating the operation result.
1259 *
1260 * @retval 0 Operation was successful.
1261 * @retval -RED_EBADF @p ulSrcPInode is not a valid inode number; or
1262 * @p ulDstPInode is not a valid inode number.
1263 * @retval -RED_EEXIST #REDCONF_RENAME_POSIX is false and the
1264 * destination name exists.
1265 * @retval -RED_EINVAL The volume is not mounted; @p pszSrcName is
1266 * `NULL`; or @p pszDstName is `NULL`.
1267 * @retval -RED_EIO A disk I/O error occurred.
1268 * @retval -RED_EISDIR The destination name exists and is a directory,
1269 * and the source name is a non-directory.
1270 * @retval -RED_ENAMETOOLONG Either @p pszSrcName or @p pszDstName is longer
1271 * than #REDCONF_NAME_MAX.
1272 * @retval -RED_ENOENT The source name is not an existing entry; or
1273 * either @p pszSrcName or @p pszDstName point to
1274 * an empty string.
1275 * @retval -RED_ENOTDIR @p ulSrcPInode is not a directory; or
1276 * @p ulDstPInode is not a directory; or the source
1277 * name is a directory and the destination name is
1278 * a file.
1279 * @retval -RED_ENOTEMPTY The destination name is a directory which is not
1280 * empty.
1281 * @retval -RED_ENOSPC The file system does not have enough space to
1282 * extend the @p ulDstPInode directory.
1283 * @retval -RED_EROFS The directory to be removed resides on a
1284 * read-only file system.
1285 */
RedCoreRename(uint32_t ulSrcPInode,const char * pszSrcName,uint32_t ulDstPInode,const char * pszDstName)1286 REDSTATUS RedCoreRename( uint32_t ulSrcPInode,
1287 const char * pszSrcName,
1288 uint32_t ulDstPInode,
1289 const char * pszDstName )
1290 {
1291 REDSTATUS ret;
1292
1293 if( !gpRedVolume->fMounted )
1294 {
1295 ret = -RED_EINVAL;
1296 }
1297 else if( gpRedVolume->fReadOnly )
1298 {
1299 ret = -RED_EROFS;
1300 }
1301 else
1302 {
1303 ret = CoreRename( ulSrcPInode, pszSrcName, ulDstPInode, pszDstName );
1304
1305 if( ( ret == -RED_ENOSPC ) &&
1306 ( ( gpRedVolume->ulTransMask & RED_TRANSACT_VOLFULL ) != 0U ) &&
1307 ( gpRedCoreVol->ulAlmostFreeBlocks > 0U ) )
1308 {
1309 ret = RedVolTransact();
1310
1311 if( ret == 0 )
1312 {
1313 ret = CoreRename( ulSrcPInode, pszSrcName, ulDstPInode, pszDstName );
1314 }
1315 }
1316
1317 if( ( ret == 0 ) && ( ( gpRedVolume->ulTransMask & RED_TRANSACT_RENAME ) != 0U ) )
1318 {
1319 ret = RedVolTransact();
1320 }
1321 }
1322
1323 return ret;
1324 }
1325
1326
1327 /** @brief Rename a file or directory.
1328 *
1329 * @param ulSrcPInode The inode number of the parent directory of the file or
1330 * directory to rename.
1331 * @param pszSrcName The name of the file or directory to rename.
1332 * @param ulDstPInode The new parent directory inode number of the file or
1333 * directory after the rename.
1334 * @param pszDstName The new name of the file or directory after the rename.
1335 *
1336 * @return A negated ::REDSTATUS code indicating the operation result.
1337 *
1338 * @retval 0 Operation was successful.
1339 * @retval -RED_EBADF @p ulSrcPInode is not a valid inode number; or
1340 * @p ulDstPInode is not a valid inode number.
1341 * @retval -RED_EEXIST #REDCONF_RENAME_POSIX is false and the
1342 * destination name exists.
1343 * @retval -RED_EIO A disk I/O error occurred.
1344 * @retval -RED_EISDIR The destination name exists and is a directory,
1345 * and the source name is a non-directory.
1346 * @retval -RED_ENAMETOOLONG Either @p pszSrcName or @p pszDstName is longer
1347 * than #REDCONF_NAME_MAX.
1348 * @retval -RED_ENOENT The source name is not an existing entry; or
1349 * either @p pszSrcName or @p pszDstName point to
1350 * an empty string.
1351 * @retval -RED_ENOTDIR @p ulSrcPInode is not a directory; or
1352 * @p ulDstPInode is not a directory; or the source
1353 * name is a directory and the destination name is
1354 * a file.
1355 * @retval -RED_ENOTEMPTY The destination name is a directory which is not
1356 * empty.
1357 * @retval -RED_ENOSPC The file system does not have enough space to
1358 * extend the @p ulDstPInode directory.
1359 * @retval -RED_EROFS The directory to be removed resides on a
1360 * read-only file system.
1361 */
CoreRename(uint32_t ulSrcPInode,const char * pszSrcName,uint32_t ulDstPInode,const char * pszDstName)1362 static REDSTATUS CoreRename( uint32_t ulSrcPInode,
1363 const char * pszSrcName,
1364 uint32_t ulDstPInode,
1365 const char * pszDstName )
1366 {
1367 REDSTATUS ret;
1368
1369 if( gpRedVolume->fReadOnly )
1370 {
1371 ret = -RED_EROFS;
1372 }
1373 else
1374 {
1375 bool fUpdateTimestamps = false;
1376 CINODE SrcPInode;
1377
1378 SrcPInode.ulInode = ulSrcPInode;
1379 ret = RedInodeMount( &SrcPInode, FTYPE_DIR, true );
1380
1381 if( ret == 0 )
1382 {
1383 CINODE DstPInode;
1384 CINODE * pDstPInode;
1385
1386 if( ulSrcPInode == ulDstPInode )
1387 {
1388 pDstPInode = &SrcPInode;
1389 }
1390 else
1391 {
1392 pDstPInode = &DstPInode;
1393 DstPInode.ulInode = ulDstPInode;
1394 ret = RedInodeMount( pDstPInode, FTYPE_DIR, true );
1395 }
1396
1397 if( ret == 0 )
1398 {
1399 /* Initialize these to zero so we can unconditionally put them,
1400 * even if RedDirEntryRename() fails before mounting them.
1401 */
1402 CINODE SrcInode = { 0U };
1403 CINODE DstInode = { 0U };
1404
1405 ret = RedDirEntryRename( &SrcPInode, pszSrcName, &SrcInode, pDstPInode, pszDstName, &DstInode );
1406
1407 #if REDCONF_RENAME_ATOMIC == 1
1408 if( ( ret == 0 ) && ( DstInode.ulInode != INODE_INVALID ) && ( DstInode.ulInode != SrcInode.ulInode ) )
1409 {
1410 /* If the inode is deleted, buffers are needed to read all
1411 * of the indirects and free the data blocks. Before doing
1412 * that, to reduce the minimum number of buffers needed to
1413 * complete the rename, release parent directory inode
1414 * buffers which are no longer needed.
1415 */
1416 RedInodePutCoord( &SrcPInode );
1417 RedInodePutCoord( pDstPInode );
1418
1419 ret = RedInodeLinkDec( &DstInode );
1420 CRITICAL_ASSERT( ret == 0 );
1421 }
1422
1423 if( ( ret == 0 ) && ( DstInode.ulInode != SrcInode.ulInode ) )
1424 #else /* if REDCONF_RENAME_ATOMIC == 1 */
1425 if( ret == 0 )
1426 #endif /* if REDCONF_RENAME_ATOMIC == 1 */
1427 {
1428 fUpdateTimestamps = true;
1429 }
1430
1431 #if REDCONF_RENAME_ATOMIC == 1
1432 RedInodePut( &DstInode, 0U );
1433 #endif
1434
1435 /* POSIX says updating ctime for the source inode is optional,
1436 * but searching around it looks like this is common for Linux
1437 * and other Unix file systems.
1438 */
1439 RedInodePut( &SrcInode, fUpdateTimestamps ? IPUT_UPDATE_CTIME : 0U );
1440 RedInodePut( pDstPInode, fUpdateTimestamps ? ( uint8_t ) ( IPUT_UPDATE_MTIME | IPUT_UPDATE_CTIME ) : 0U );
1441 }
1442 }
1443
1444 RedInodePut( &SrcPInode, fUpdateTimestamps ? ( uint8_t ) ( IPUT_UPDATE_MTIME | IPUT_UPDATE_CTIME ) : 0U );
1445 }
1446
1447 return ret;
1448 }
1449 #endif /* (REDCONF_READ_ONLY == 0) && (REDCONF_API_POSIX == 1) && (REDCONF_API_POSIX_RENAME == 1) */
1450
1451
1452 #if REDCONF_API_POSIX == 1
1453
1454 /** @brief Get the status of a file or directory.
1455 *
1456 * See the ::REDSTAT type for the details of the information returned.
1457 *
1458 * @param ulInode The inode number of the file or directory whose information
1459 * is to be retrieved.
1460 * @param pStat Pointer to a ::REDSTAT buffer to populate.
1461 *
1462 * @return A negated ::REDSTATUS code indicating the operation result.
1463 *
1464 * @retval 0 Operation was successful.
1465 * @retval -RED_EBADF @p ulInode is not a valid inode.
1466 * @retval -RED_EINVAL The volume is not mounted; @p pStat is `NULL`.
1467 * @retval -RED_EIO A disk I/O error occurred.
1468 */
RedCoreStat(uint32_t ulInode,REDSTAT * pStat)1469 REDSTATUS RedCoreStat( uint32_t ulInode,
1470 REDSTAT * pStat )
1471 {
1472 REDSTATUS ret;
1473
1474 if( !gpRedVolume->fMounted || ( pStat == NULL ) )
1475 {
1476 ret = -RED_EINVAL;
1477 }
1478 else
1479 {
1480 CINODE ino;
1481
1482 ino.ulInode = ulInode;
1483 ret = RedInodeMount( &ino, FTYPE_EITHER, false );
1484
1485 if( ret == 0 )
1486 {
1487 RedMemSet( pStat, 0U, sizeof( *pStat ) );
1488
1489 pStat->st_dev = gbRedVolNum;
1490 pStat->st_ino = ulInode;
1491 pStat->st_mode = ino.pInodeBuf->uMode;
1492 #if REDCONF_API_POSIX_LINK == 1
1493 pStat->st_nlink = ino.pInodeBuf->uNLink;
1494 #else
1495 pStat->st_nlink = 1U;
1496 #endif
1497 pStat->st_size = ino.pInodeBuf->ullSize;
1498 #if REDCONF_INODE_TIMESTAMPS == 1
1499 pStat->st_atime = ino.pInodeBuf->ulATime;
1500 pStat->st_mtime = ino.pInodeBuf->ulMTime;
1501 pStat->st_ctime = ino.pInodeBuf->ulCTime;
1502 #endif
1503 #if REDCONF_INODE_BLOCKS == 1
1504 pStat->st_blocks = ino.pInodeBuf->ulBlocks;
1505 #endif
1506
1507 RedInodePut( &ino, 0U );
1508 }
1509 }
1510
1511 return ret;
1512 }
1513 #endif /* REDCONF_API_POSIX == 1 */
1514
1515
1516 #if REDCONF_API_FSE == 1
1517
1518 /** @brief Get the size of a file.
1519 *
1520 * @param ulInode The inode number of the file whose size is to be retrieved.
1521 * @param pullSize On successful exit, populated with the file size.
1522 *
1523 * @return A negated ::REDSTATUS code indicating the operation result.
1524 *
1525 * @retval 0 Operation was successful.
1526 * @retval -RED_EBADF @p ulInode is not a valid inode.
1527 * @retval -RED_EINVAL The volume is not mounted; @p pullSize is `NULL`.
1528 * @retval -RED_EIO A disk I/O error occurred.
1529 * @retval -RED_EISDIR @p ulInode is a directory inode.
1530 */
RedCoreFileSizeGet(uint32_t ulInode,uint64_t * pullSize)1531 REDSTATUS RedCoreFileSizeGet( uint32_t ulInode,
1532 uint64_t * pullSize )
1533 {
1534 REDSTATUS ret;
1535
1536 if( !gpRedVolume->fMounted || ( pullSize == NULL ) )
1537 {
1538 ret = -RED_EINVAL;
1539 }
1540 else
1541 {
1542 CINODE ino;
1543
1544 ino.ulInode = ulInode;
1545 ret = RedInodeMount( &ino, FTYPE_FILE, false );
1546
1547 if( ret == 0 )
1548 {
1549 *pullSize = ino.pInodeBuf->ullSize;
1550
1551 RedInodePut( &ino, 0U );
1552 }
1553 }
1554
1555 return ret;
1556 }
1557 #endif /* REDCONF_API_FSE == 1 */
1558
1559
1560 /** @brief Read from a file.
1561 *
1562 * Data which has not yet been written, but which is before the end-of-file
1563 * (sparse data), shall read as zeroes. A short read -- where the number of
1564 * bytes read is less than requested -- indicates that the requested read was
1565 * partially or, if zero bytes were read, entirely beyond the end-of-file.
1566 *
1567 * If @p ullStart is at or beyond the maximum file size, it is treated like
1568 * any other read entirely beyond the end-of-file: no data is read and
1569 * @p pulLen is populated with zero.
1570 *
1571 * @param ulInode The inode number of the file to read.
1572 * @param ullStart The file offset to read from.
1573 * @param pulLen On entry, contains the number of bytes to read; on
1574 * successful exit, contains the number of bytes actually
1575 * read.
1576 * @param pBuffer The buffer to populate with the data read. Must be big
1577 * enough for the read request.
1578 *
1579 * @return A negated ::REDSTATUS code indicating the operation result.
1580 *
1581 * @retval 0 Operation was successful.
1582 * @retval -RED_EBADF @p ulInode is not a valid inode number.
1583 * @retval -RED_EINVAL The volume is not mounted; or @p pBuffer is `NULL`.
1584 * @retval -RED_EIO A disk I/O error occurred.
1585 * @retval -RED_EISDIR The inode is a directory inode.
1586 */
RedCoreFileRead(uint32_t ulInode,uint64_t ullStart,uint32_t * pulLen,void * pBuffer)1587 REDSTATUS RedCoreFileRead( uint32_t ulInode,
1588 uint64_t ullStart,
1589 uint32_t * pulLen,
1590 void * pBuffer )
1591 {
1592 REDSTATUS ret;
1593
1594 if( !gpRedVolume->fMounted || ( pulLen == NULL ) )
1595 {
1596 ret = -RED_EINVAL;
1597 }
1598 else
1599 {
1600 #if ( REDCONF_ATIME == 1 ) && ( REDCONF_READ_ONLY == 0 )
1601 bool fUpdateAtime = ( *pulLen > 0U ) && !gpRedVolume->fReadOnly;
1602 #else
1603 bool fUpdateAtime = false;
1604 #endif
1605 CINODE ino;
1606
1607 ino.ulInode = ulInode;
1608 ret = RedInodeMount( &ino, FTYPE_FILE, fUpdateAtime );
1609
1610 if( ret == 0 )
1611 {
1612 ret = RedInodeDataRead( &ino, ullStart, pulLen, pBuffer );
1613
1614 #if ( REDCONF_ATIME == 1 ) && ( REDCONF_READ_ONLY == 0 )
1615 RedInodePut( &ino, ( ( ret == 0 ) && fUpdateAtime ) ? IPUT_UPDATE_ATIME : 0U );
1616 #else
1617 RedInodePut( &ino, 0U );
1618 #endif
1619 }
1620 }
1621
1622 return ret;
1623 }
1624
1625
1626 #if REDCONF_READ_ONLY == 0
1627
1628 /** @brief Write to a file.
1629 *
1630 * If the write extends beyond the end-of-file, the file size will be
1631 * increased.
1632 *
1633 * A short write -- where the number of bytes written is less than requested
1634 * -- indicates either that the file system ran out of space but was still
1635 * able to write some of the request; or that the request would have caused
1636 * the file to exceed the maximum file size, but some of the data could be
1637 * written prior to the file size limit.
1638 *
1639 * If an error is returned, either none of the data was written or a critical
1640 * error occurred (like an I/O error) and the file system volume will be
1641 * read-only.
1642 *
1643 * @param ulInode The file number of the file to write.
1644 * @param ullStart The file offset to write at.
1645 * @param pulLen On entry, the number of bytes to write; on successful exit,
1646 * the number of bytes actually written.
1647 * @param pBuffer The buffer containing the data to be written. Must big
1648 * enough for the write request.
1649 *
1650 * @return A negated ::REDSTATUS code indicating the operation result.
1651 *
1652 * @retval 0 Operation was successful.
1653 * @retval -RED_EBADF @p ulInode is not a valid file number.
1654 * @retval -RED_EFBIG No data can be written to the given file offset since
1655 * the resulting file size would exceed the maximum file
1656 * size.
1657 * @retval -RED_EINVAL The volume is not mounted; or @p pBuffer is `NULL`.
1658 * @retval -RED_EIO A disk I/O error occurred.
1659 * @retval -RED_EISDIR The inode is a directory inode.
1660 * @retval -RED_ENOSPC No data can be written because there is insufficient
1661 * free space.
1662 * @retval -RED_EROFS The file system volume is read-only.
1663 */
RedCoreFileWrite(uint32_t ulInode,uint64_t ullStart,uint32_t * pulLen,const void * pBuffer)1664 REDSTATUS RedCoreFileWrite( uint32_t ulInode,
1665 uint64_t ullStart,
1666 uint32_t * pulLen,
1667 const void * pBuffer )
1668 {
1669 REDSTATUS ret;
1670
1671 if( !gpRedVolume->fMounted )
1672 {
1673 ret = -RED_EINVAL;
1674 }
1675 else if( gpRedVolume->fReadOnly )
1676 {
1677 ret = -RED_EROFS;
1678 }
1679 else
1680 {
1681 ret = CoreFileWrite( ulInode, ullStart, pulLen, pBuffer );
1682
1683 if( ( ret == -RED_ENOSPC ) &&
1684 ( ( gpRedVolume->ulTransMask & RED_TRANSACT_VOLFULL ) != 0U ) &&
1685 ( gpRedCoreVol->ulAlmostFreeBlocks > 0U ) )
1686 {
1687 ret = RedVolTransact();
1688
1689 if( ret == 0 )
1690 {
1691 ret = CoreFileWrite( ulInode, ullStart, pulLen, pBuffer );
1692 }
1693 }
1694
1695 if( ( ret == 0 ) && ( ( gpRedVolume->ulTransMask & RED_TRANSACT_WRITE ) != 0U ) )
1696 {
1697 ret = RedVolTransact();
1698 }
1699 }
1700
1701 return ret;
1702 }
1703
1704
1705 /** @brief Write to a file.
1706 *
1707 * @param ulInode The file number of the file to write.
1708 * @param ullStart The file offset to write at.
1709 * @param pulLen On entry, the number of bytes to write; on successful exit,
1710 * the number of bytes actually written.
1711 * @param pBuffer The buffer containing the data to be written. Must big
1712 * enough for the write request.
1713 *
1714 * @return A negated ::REDSTATUS code indicating the operation result.
1715 *
1716 * @retval 0 Operation was successful.
1717 * @retval -RED_EBADF @p ulInode is not a valid file number.
1718 * @retval -RED_EFBIG No data can be written to the given file offset since
1719 * the resulting file size would exceed the maximum file
1720 * size.
1721 * @retval -RED_EIO A disk I/O error occurred.
1722 * @retval -RED_EISDIR The inode is a directory inode.
1723 * @retval -RED_ENOSPC No data can be written because there is insufficient
1724 * free space.
1725 * @retval -RED_EROFS The file system volume is read-only.
1726 */
CoreFileWrite(uint32_t ulInode,uint64_t ullStart,uint32_t * pulLen,const void * pBuffer)1727 static REDSTATUS CoreFileWrite( uint32_t ulInode,
1728 uint64_t ullStart,
1729 uint32_t * pulLen,
1730 const void * pBuffer )
1731 {
1732 REDSTATUS ret;
1733
1734 if( gpRedVolume->fReadOnly )
1735 {
1736 ret = -RED_EROFS;
1737 }
1738 else
1739 {
1740 CINODE ino;
1741
1742 ino.ulInode = ulInode;
1743 ret = RedInodeMount( &ino, FTYPE_FILE, true );
1744
1745 if( ret == 0 )
1746 {
1747 ret = RedInodeDataWrite( &ino, ullStart, pulLen, pBuffer );
1748
1749 RedInodePut( &ino, ( ret == 0 ) ? ( uint8_t ) ( IPUT_UPDATE_MTIME | IPUT_UPDATE_CTIME ) : 0U );
1750 }
1751 }
1752
1753 return ret;
1754 }
1755 #endif /* REDCONF_READ_ONLY == 0 */
1756
1757
1758 #if TRUNCATE_SUPPORTED
1759
1760 /** @brief Set the file size.
1761 *
1762 * Allows the file size to be increased, decreased, or to remain the same. If
1763 * the file size is increased, the new area is sparse (will read as zeroes).
1764 * If the file size is decreased, the data beyond the new end-of-file will
1765 * return to free space once it is no longer part of the committed state
1766 * (either immediately or after the next transaction point).
1767 *
1768 * @param ulInode The inode of the file to truncate.
1769 * @param ullSize The new file size, in bytes.
1770 *
1771 * @return A negated ::REDSTATUS code indicating the operation result.
1772 *
1773 * @retval 0 Operation was successful.
1774 * @retval -RED_EBADF @p ulInode is not a valid inode number.
1775 * @retval -RED_EFBIG @p ullSize exceeds the maximum file size.
1776 * @retval -RED_EINVAL The volume is not mounted.
1777 * @retval -RED_EIO A disk I/O error occurred.
1778 * @retval -RED_EISDIR The inode is a directory inode.
1779 * @retval -RED_ENOSPC Insufficient free space to perform the truncate.
1780 * @retval -RED_EROFS The file system volume is read-only.
1781 */
RedCoreFileTruncate(uint32_t ulInode,uint64_t ullSize)1782 REDSTATUS RedCoreFileTruncate( uint32_t ulInode,
1783 uint64_t ullSize )
1784 {
1785 REDSTATUS ret;
1786
1787 if( !gpRedVolume->fMounted )
1788 {
1789 ret = -RED_EINVAL;
1790 }
1791 else if( gpRedVolume->fReadOnly )
1792 {
1793 ret = -RED_EROFS;
1794 }
1795 else
1796 {
1797 ret = CoreFileTruncate( ulInode, ullSize );
1798
1799 if( ( ret == -RED_ENOSPC ) &&
1800 ( ( gpRedVolume->ulTransMask & RED_TRANSACT_VOLFULL ) != 0U ) &&
1801 ( gpRedCoreVol->ulAlmostFreeBlocks > 0U ) )
1802 {
1803 ret = RedVolTransact();
1804
1805 if( ret == 0 )
1806 {
1807 ret = CoreFileTruncate( ulInode, ullSize );
1808 }
1809 }
1810
1811 if( ( ret == 0 ) && ( ( gpRedVolume->ulTransMask & RED_TRANSACT_TRUNCATE ) != 0U ) )
1812 {
1813 ret = RedVolTransact();
1814 }
1815 }
1816
1817 return ret;
1818 }
1819
1820
1821 /** @brief Set the file size.
1822 *
1823 * @param ulInode The inode of the file to truncate.
1824 * @param ullSize The new file size, in bytes.
1825 *
1826 * @return A negated ::REDSTATUS code indicating the operation result.
1827 *
1828 * @retval 0 Operation was successful.
1829 * @retval -RED_EBADF @p ulInode is not a valid inode number.
1830 * @retval -RED_EFBIG @p ullSize exceeds the maximum file size.
1831 * @retval -RED_EIO A disk I/O error occurred.
1832 * @retval -RED_EISDIR The inode is a directory inode.
1833 * @retval -RED_ENOSPC Insufficient free space to perform the truncate.
1834 * @retval -RED_EROFS The file system volume is read-only.
1835 */
CoreFileTruncate(uint32_t ulInode,uint64_t ullSize)1836 static REDSTATUS CoreFileTruncate( uint32_t ulInode,
1837 uint64_t ullSize )
1838 {
1839 REDSTATUS ret;
1840
1841 if( gpRedVolume->fReadOnly )
1842 {
1843 ret = -RED_EROFS;
1844 }
1845 else
1846 {
1847 CINODE ino;
1848
1849 ino.ulInode = ulInode;
1850 ret = RedInodeMount( &ino, FTYPE_FILE, true );
1851
1852 if( ret == 0 )
1853 {
1854 #if RESERVED_BLOCKS > 0U
1855 gpRedCoreVol->fUseReservedBlocks = ( ullSize < ino.pInodeBuf->ullSize );
1856 #endif
1857
1858 ret = RedInodeDataTruncate( &ino, ullSize );
1859
1860 #if RESERVED_BLOCKS > 0U
1861 gpRedCoreVol->fUseReservedBlocks = false;
1862 #endif
1863
1864 RedInodePut( &ino, ( ret == 0 ) ? ( uint8_t ) ( IPUT_UPDATE_MTIME | IPUT_UPDATE_CTIME ) : 0U );
1865 }
1866 }
1867
1868 return ret;
1869 }
1870 #endif /* TRUNCATE_SUPPORTED */
1871
1872
1873 #if ( REDCONF_API_POSIX == 1 ) && ( REDCONF_API_POSIX_READDIR == 1 )
1874
1875 /** @brief Read from a directory.
1876 *
1877 * If files are added to the directory after it is opened, the new files may
1878 * or may not be returned by this function. If files are deleted, the deleted
1879 * files will not be returned.
1880 *
1881 * @param ulInode The directory inode to read from.
1882 * @param pulPos A token which stores the position within the directory. To
1883 * read from the beginning of the directory, populate with
1884 * zero.
1885 * @param pszName Pointer to a buffer which must be big enough to store a
1886 * maximum size name, including a null terminator. On
1887 * successful exit, populated with the name of the next
1888 * directory entry.
1889 * @param pulInode On successful return, populated with the inode number of the
1890 * next directory entry.
1891 *
1892 * @return A negated ::REDSTATUS code indicating the operation result.
1893 *
1894 * @retval 0 Operation was successful.
1895 * @retval -RED_EBADF @p ulInode is not a valid inode number.
1896 * @retval -RED_EINVAL The volume is not mounted.
1897 * @retval -RED_EIO A disk I/O error occurred.
1898 * @retval -RED_ENOENT There are no more entries in the directory.
1899 * @retval -RED_ENOTDIR @p ulInode refers to a file.
1900 */
RedCoreDirRead(uint32_t ulInode,uint32_t * pulPos,char * pszName,uint32_t * pulInode)1901 REDSTATUS RedCoreDirRead( uint32_t ulInode,
1902 uint32_t * pulPos,
1903 char * pszName,
1904 uint32_t * pulInode )
1905 {
1906 REDSTATUS ret;
1907
1908 if( !gpRedVolume->fMounted )
1909 {
1910 ret = -RED_EINVAL;
1911 }
1912 else
1913 {
1914 CINODE ino;
1915
1916 ino.ulInode = ulInode;
1917 ret = RedInodeMount( &ino, FTYPE_DIR, false );
1918
1919 if( ret == 0 )
1920 {
1921 ret = RedDirEntryRead( &ino, pulPos, pszName, pulInode );
1922
1923 #if ( REDCONF_ATIME == 1 ) && ( REDCONF_READ_ONLY == 0 )
1924 if( ( ret == 0 ) && !gpRedVolume->fReadOnly )
1925 {
1926 ret = RedInodeBranch( &ino );
1927 }
1928
1929 RedInodePut( &ino, ( ( ret == 0 ) && !gpRedVolume->fReadOnly ) ? IPUT_UPDATE_ATIME : 0U );
1930 #else
1931 RedInodePut( &ino, 0U );
1932 #endif
1933 }
1934 }
1935
1936 return ret;
1937 }
1938 #endif /* (REDCONF_API_POSIX == 1) && (REDCONF_API_POSIX_READDIR == 1) */
1939