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 Implementation of the the Reliance Edge POSIX-like API. 29 */ 30 31 #include <redfs.h> 32 33 #if REDCONF_API_POSIX == 1 34 35 /** @defgroup red_group_posix The POSIX-like File System Interface 36 * @{ 37 */ 38 39 #include <redvolume.h> 40 #include <redcoreapi.h> 41 #include <redposix.h> 42 #include <redpath.h> 43 44 45 /*------------------------------------------------------------------- 46 * File Descriptors 47 * -------------------------------------------------------------------*/ 48 49 #define FD_GEN_BITS 11U /* File descriptor bits for mount generation. */ 50 #define FD_VOL_BITS 8U /* File descriptor bits for volume number. */ 51 #define FD_IDX_BITS 12U /* File descriptor bits for handle index. */ 52 53 /* 31 bits available: file descriptors are int32_t, but the sign bit must 54 * always be zero. 55 */ 56 #if ( FD_GEN_BITS + FD_VOL_BITS + FD_IDX_BITS ) > 31U 57 #error "Internal error: too many file descriptor bits!" 58 #endif 59 60 /* Maximum values for file descriptor components. 61 */ 62 #define FD_GEN_MAX ( ( 1UL << FD_GEN_BITS ) - 1U ) 63 #define FD_VOL_MAX ( ( 1UL << FD_VOL_BITS ) - 1U ) 64 #define FD_IDX_MAX ( ( 1UL << FD_IDX_BITS ) - 1U ) 65 66 #if REDCONF_VOLUME_COUNT > FD_VOL_MAX 67 #error "Error: Too many file system volumes!" 68 #endif 69 #if REDCONF_HANDLE_COUNT > ( FD_IDX_MAX + 1U ) 70 #error "Error: Too many file system handles!" 71 #endif 72 73 /* File descriptors must never be negative; and must never be zero, one, or 74 * two, to avoid confusion with STDIN, STDOUT, and STDERR. 75 */ 76 #define FD_MIN ( 3 ) 77 78 /*------------------------------------------------------------------- 79 * Handles 80 * -------------------------------------------------------------------*/ 81 82 /* Mask of all RED_O_* values. 83 */ 84 #define RED_O_MASK ( RED_O_RDONLY | RED_O_WRONLY | RED_O_RDWR | RED_O_APPEND | RED_O_CREAT | RED_O_EXCL | RED_O_TRUNC ) 85 86 #define HFLAG_DIRECTORY 0x01U /* Handle is for a directory. */ 87 #define HFLAG_READABLE 0x02U /* Handle is readable. */ 88 #define HFLAG_WRITEABLE 0x04U /* Handle is writeable. */ 89 #define HFLAG_APPENDING 0x08U /* Handle was opened in append mode. */ 90 91 /* @brief Handle structure, used to implement file descriptors and directory 92 * streams. 93 */ 94 typedef struct sREDHANDLE 95 { 96 uint32_t ulInode; /**< Inode number; 0 if handle is available. */ 97 uint8_t bVolNum; /**< Volume containing the inode. */ 98 uint8_t bFlags; /**< Handle flags (type and mode). */ 99 uint64_t ullOffset; /**< File or directory offset. */ 100 #if REDCONF_API_POSIX_READDIR == 1 101 REDDIRENT dirent; /**< Dirent structure returned by red_readdir(). */ 102 #endif 103 } REDHANDLE; 104 105 /*------------------------------------------------------------------- 106 * Tasks 107 * -------------------------------------------------------------------*/ 108 109 #if REDCONF_TASK_COUNT > 1U 110 111 /* @brief Per-task information. 112 */ 113 typedef struct 114 { 115 uint32_t ulTaskId; /**< ID of the task which owns this slot; 0 if free. */ 116 REDSTATUS iErrno; /**< Last error value. */ 117 } TASKSLOT; 118 #endif 119 120 /*------------------------------------------------------------------- 121 * Local Prototypes 122 * -------------------------------------------------------------------*/ 123 124 #if ( REDCONF_READ_ONLY == 0 ) && ( ( REDCONF_API_POSIX_UNLINK == 1 ) || ( REDCONF_API_POSIX_RMDIR == 1 ) ) 125 static REDSTATUS UnlinkSub( const char * pszPath, 126 FTYPE type ); 127 #endif 128 static REDSTATUS FildesOpen( const char * pszPath, 129 uint32_t ulOpenMode, 130 FTYPE type, 131 int32_t * piFildes ); 132 static REDSTATUS FildesClose( int32_t iFildes ); 133 static REDSTATUS FildesToHandle( int32_t iFildes, 134 FTYPE expectedType, 135 REDHANDLE ** ppHandle ); 136 static int32_t FildesPack( uint16_t uHandleIdx, 137 uint8_t bVolNum ); 138 static void FildesUnpack( int32_t iFildes, 139 uint16_t * puHandleIdx, 140 uint8_t * pbVolNum, 141 uint16_t * puGeneration ); 142 #if REDCONF_API_POSIX_READDIR == 1 143 static bool DirStreamIsValid( const REDDIR * pDirStream ); 144 #endif 145 static REDSTATUS PosixEnter( void ); 146 static void PosixLeave( void ); 147 static REDSTATUS ModeTypeCheck( uint16_t uMode, 148 FTYPE expectedType ); 149 #if ( REDCONF_READ_ONLY == 0 ) && ( ( REDCONF_API_POSIX_UNLINK == 1 ) || ( REDCONF_API_POSIX_RMDIR == 1 ) || ( ( REDCONF_API_POSIX_RENAME == 1 ) && ( REDCONF_RENAME_ATOMIC == 1 ) ) ) 150 static REDSTATUS InodeUnlinkCheck( uint32_t ulInode ); 151 #endif 152 #if REDCONF_TASK_COUNT > 1U 153 static REDSTATUS TaskRegister( uint32_t * pulTaskIdx ); 154 #endif 155 static int32_t PosixReturn( REDSTATUS iError ); 156 157 /*------------------------------------------------------------------- 158 * Globals 159 * -------------------------------------------------------------------*/ 160 161 static bool gfPosixInited; /* Whether driver is initialized. */ 162 static REDHANDLE gaHandle[ REDCONF_HANDLE_COUNT ]; /* Array of all handles. */ 163 #if REDCONF_TASK_COUNT > 1U 164 static TASKSLOT gaTask[ REDCONF_TASK_COUNT ]; /* Array of task slots. */ 165 #endif 166 167 /* Array of volume mount "generations". These are incremented for a volume 168 * each time that volume is mounted. The generation number (along with the 169 * volume number) is incorporated into the file descriptors; a stale file 170 * descriptor from a previous mount can be detected since it will include a 171 * stale generation number. 172 */ 173 static uint16_t gauGeneration[ REDCONF_VOLUME_COUNT ]; 174 175 176 /*------------------------------------------------------------------- 177 * Public API 178 * -------------------------------------------------------------------*/ 179 180 /** @brief Initialize the Reliance Edge file system driver. 181 * 182 * Prepares the Reliance Edge file system driver to be used. Must be the first 183 * Reliance Edge function to be invoked: no volumes can be mounted or formatted 184 * until the driver has been initialized. 185 * 186 * If this function is called when the Reliance Edge driver is already 187 * initialized, it does nothing and returns success. 188 * 189 * This function is not thread safe: attempting to initialize from multiple 190 * threads could leave things in a bad state. 191 * 192 * @return On success, zero is returned. On error, -1 is returned and 193 #red_errno is set appropriately. 194 * 195 * <b>Errno values</b> 196 * - #RED_EINVAL: The volume path prefix configuration is invalid. 197 */ red_init(void)198 int32_t red_init( void ) 199 { 200 REDSTATUS ret; 201 202 if( gfPosixInited ) 203 { 204 ret = 0; 205 } 206 else 207 { 208 ret = RedCoreInit(); 209 210 if( ret == 0 ) 211 { 212 RedMemSet( gaHandle, 0U, sizeof( gaHandle ) ); 213 214 #if REDCONF_TASK_COUNT > 1U 215 RedMemSet( gaTask, 0U, sizeof( gaTask ) ); 216 #endif 217 218 gfPosixInited = true; 219 } 220 } 221 222 return PosixReturn( ret ); 223 } 224 225 226 /** @brief Uninitialize the Reliance Edge file system driver. 227 * 228 * Tears down the Reliance Edge file system driver. Cannot be used until all 229 * Reliance Edge volumes are unmounted. A subsequent call to red_init() will 230 * initialize the driver again. 231 * 232 * If this function is called when the Reliance Edge driver is already 233 * uninitialized, it does nothing and returns success. 234 * 235 * This function is not thread safe: attempting to uninitialize from multiple 236 * threads could leave things in a bad state. 237 * 238 * @return On success, zero is returned. On error, -1 is returned and 239 #red_errno is set appropriately. 240 * 241 * <b>Errno values</b> 242 * - #RED_EBUSY: At least one volume is still mounted. 243 */ red_uninit(void)244 int32_t red_uninit( void ) 245 { 246 REDSTATUS ret; 247 248 if( gfPosixInited ) 249 { 250 ret = PosixEnter(); 251 252 if( ret == 0 ) 253 { 254 uint8_t bVolNum; 255 256 for( bVolNum = 0U; bVolNum < REDCONF_VOLUME_COUNT; bVolNum++ ) 257 { 258 if( gaRedVolume[ bVolNum ].fMounted ) 259 { 260 ret = -RED_EBUSY; 261 break; 262 } 263 } 264 265 if( ret == 0 ) 266 { 267 /* All volumes are unmounted. Mark the driver as 268 * uninitialized before releasing the FS mutex, to avoid any 269 * race condition where a volume could be mounted and then the 270 * driver uninitialized with a mounted volume. 271 */ 272 gfPosixInited = false; 273 } 274 275 /* The FS mutex must be released before we uninitialize the core, 276 * since the FS mutex needs to be in the released state when it 277 * gets uninitialized. 278 * 279 * Don't use PosixLeave(), since it asserts gfPosixInited is true. 280 */ 281 #if REDCONF_TASK_COUNT > 1U 282 RedOsMutexRelease(); 283 #endif 284 } 285 286 if( ret == 0 ) 287 { 288 ret = RedCoreUninit(); 289 290 /* Not good if the above fails, since things might be partly, but 291 * not entirely, torn down, and there might not be a way back to 292 * a valid driver state. 293 */ 294 REDASSERT( ret == 0 ); 295 } 296 } 297 else 298 { 299 ret = 0; 300 } 301 302 return PosixReturn( ret ); 303 } 304 305 306 /** @brief Mount a file system volume. 307 * 308 * Prepares the file system volume to be accessed. Mount will fail if the 309 * volume has never been formatted, or if the on-disk format is inconsistent 310 * with the compile-time configuration. 311 * 312 * An error is returned if the volume is already mounted. 313 * 314 * @param pszVolume A path prefix identifying the volume to mount. 315 * 316 * @return On success, zero is returned. On error, -1 is returned and 317 #red_errno is set appropriately. 318 * 319 * <b>Errno values</b> 320 * - #RED_EBUSY: Volume is already mounted. 321 * - #RED_EINVAL: @p pszVolume is `NULL`; or the driver is uninitialized. 322 * - #RED_EIO: Volume not formatted, improperly formatted, or corrupt. 323 * - #RED_ENOENT: @p pszVolume is not a valid volume path prefix. 324 * - #RED_EUSERS: Cannot become a file system user: too many users. 325 */ red_mount(const char * pszVolume)326 int32_t red_mount( const char * pszVolume ) 327 { 328 REDSTATUS ret; 329 330 ret = PosixEnter(); 331 332 if( ret == 0 ) 333 { 334 uint8_t bVolNum; 335 336 ret = RedPathSplit( pszVolume, &bVolNum, NULL ); 337 338 /* The core will return success if the volume is already mounted, so 339 * check for that condition here to propagate the error. 340 */ 341 if( ( ret == 0 ) && gaRedVolume[ bVolNum ].fMounted ) 342 { 343 ret = -RED_EBUSY; 344 } 345 346 #if REDCONF_VOLUME_COUNT > 1U 347 if( ret == 0 ) 348 { 349 ret = RedCoreVolSetCurrent( bVolNum ); 350 } 351 #endif 352 353 if( ret == 0 ) 354 { 355 ret = RedCoreVolMount(); 356 } 357 358 if( ret == 0 ) 359 { 360 /* Increment the mount generation, invalidating file descriptors 361 * from previous mounts. Note that while the generation numbers 362 * are stored in 16-bit values, we have less than 16-bits to store 363 * generations in the file descriptors, so we must wrap-around 364 * manually. 365 */ 366 gauGeneration[ bVolNum ]++; 367 368 if( gauGeneration[ bVolNum ] > FD_GEN_MAX ) 369 { 370 /* Wrap-around to one, rather than zero. The generation is 371 * stored in the top bits of the file descriptor, and doing 372 * this means that low numbers are never valid file 373 * descriptors. This implements the requirement that 0, 1, 374 * and 2 are never valid file descriptors, thereby avoiding 375 * confusion with STDIN, STDOUT, and STDERR. 376 */ 377 gauGeneration[ bVolNum ] = 1U; 378 } 379 } 380 381 PosixLeave(); 382 } 383 384 return PosixReturn( ret ); 385 } 386 387 388 /** @brief Unmount a file system volume. 389 * 390 * This function discards the in-memory state for the file system and marks it 391 * as unmounted. Subsequent attempts to access the volume will fail until the 392 * volume is mounted again. 393 * 394 * If unmount automatic transaction points are enabled, this function will 395 * commit a transaction point prior to unmounting. If unmount automatic 396 * transaction points are disabled, this function will unmount without 397 * transacting, effectively discarding the working state. 398 * 399 * Before unmounting, this function will wait for any active file system 400 * thread to complete by acquiring the FS mutex. The volume will be marked as 401 * unmounted before the FS mutex is released, so subsequent FS threads will 402 * possibly block and then see an error when attempting to access a volume 403 * which is unmounting or unmounted. If the volume has open handles, the 404 * unmount will fail. 405 * 406 * An error is returned if the volume is already unmounted. 407 * 408 * @param pszVolume A path prefix identifying the volume to unmount. 409 * 410 * @return On success, zero is returned. On error, -1 is returned and 411 #red_errno is set appropriately. 412 * 413 * <b>Errno values</b> 414 * - #RED_EBUSY: There are still open handles for this file system volume. 415 * - #RED_EINVAL: @p pszVolume is `NULL`; or the driver is uninitialized; or 416 * the volume is already unmounted. 417 * - #RED_EIO: I/O error during unmount automatic transaction point. 418 * - #RED_ENOENT: @p pszVolume is not a valid volume path prefix. 419 * - #RED_EUSERS: Cannot become a file system user: too many users. 420 */ red_umount(const char * pszVolume)421 int32_t red_umount( const char * pszVolume ) 422 { 423 REDSTATUS ret; 424 425 ret = PosixEnter(); 426 427 if( ret == 0 ) 428 { 429 uint8_t bVolNum; 430 431 ret = RedPathSplit( pszVolume, &bVolNum, NULL ); 432 433 /* The core will return success if the volume is already unmounted, so 434 * check for that condition here to propagate the error. 435 */ 436 if( ( ret == 0 ) && !gaRedVolume[ bVolNum ].fMounted ) 437 { 438 ret = -RED_EINVAL; 439 } 440 441 if( ret == 0 ) 442 { 443 uint16_t uHandleIdx; 444 445 /* Do not unmount the volume if it still has open handles. 446 */ 447 for( uHandleIdx = 0U; uHandleIdx < REDCONF_HANDLE_COUNT; uHandleIdx++ ) 448 { 449 const REDHANDLE * pHandle = &gaHandle[ uHandleIdx ]; 450 451 if( ( pHandle->ulInode != INODE_INVALID ) && ( pHandle->bVolNum == bVolNum ) ) 452 { 453 ret = -RED_EBUSY; 454 break; 455 } 456 } 457 } 458 459 #if REDCONF_VOLUME_COUNT > 1U 460 if( ret == 0 ) 461 { 462 ret = RedCoreVolSetCurrent( bVolNum ); 463 } 464 #endif 465 466 if( ret == 0 ) 467 { 468 ret = RedCoreVolUnmount(); 469 } 470 471 PosixLeave(); 472 } 473 474 return PosixReturn( ret ); 475 } 476 477 478 #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX_FORMAT == 1 ) 479 480 /** @brief Format a file system volume. 481 * 482 * Uses the statically defined volume configuration. After calling this 483 * function, the volume needs to be mounted -- see red_mount(). 484 * 485 * An error is returned if the volume is mounted. 486 * 487 * @param pszVolume A path prefix identifying the volume to format. 488 * 489 * @return On success, zero is returned. On error, -1 is returned and 490 #red_errno is set appropriately. 491 * 492 * <b>Errno values</b> 493 * - #RED_EBUSY: Volume is mounted. 494 * - #RED_EINVAL: @p pszVolume is `NULL`; or the driver is uninitialized. 495 * - #RED_EIO: I/O error formatting the volume. 496 * - #RED_ENOENT: @p pszVolume is not a valid volume path prefix. 497 * - #RED_EUSERS: Cannot become a file system user: too many users. 498 */ red_format(const char * pszVolume)499 int32_t red_format( const char * pszVolume ) 500 { 501 REDSTATUS ret; 502 503 ret = PosixEnter(); 504 505 if( ret == 0 ) 506 { 507 uint8_t bVolNum; 508 509 ret = RedPathSplit( pszVolume, &bVolNum, NULL ); 510 511 #if REDCONF_VOLUME_COUNT > 1U 512 if( ret == 0 ) 513 { 514 ret = RedCoreVolSetCurrent( bVolNum ); 515 } 516 #endif 517 518 if( ret == 0 ) 519 { 520 ret = RedCoreVolFormat(); 521 } 522 523 PosixLeave(); 524 } 525 526 return PosixReturn( ret ); 527 } 528 #endif /* if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX_FORMAT == 1 ) */ 529 530 531 #if REDCONF_READ_ONLY == 0 532 533 /** @brief Commit a transaction point. 534 * 535 * Reliance Edge is a transactional file system. All modifications, of both 536 * metadata and filedata, are initially working state. A transaction point 537 * is a process whereby the working state atomically becomes the committed 538 * state, replacing the previous committed state. Whenever Reliance Edge is 539 * mounted, including after power loss, the state of the file system after 540 * mount is the most recent committed state. Nothing from the committed 541 * state is ever missing, and nothing from the working state is ever included. 542 * 543 * @param pszVolume A path prefix identifying the volume to transact. 544 * 545 * @return On success, zero is returned. On error, -1 is returned and 546 #red_errno is set appropriately. 547 * 548 * <b>Errno values</b> 549 * - #RED_EINVAL: Volume is not mounted; or @p pszVolume is `NULL`. 550 * - #RED_EIO: I/O error during the transaction point. 551 * - #RED_ENOENT: @p pszVolume is not a valid volume path prefix. 552 * - #RED_EUSERS: Cannot become a file system user: too many users. 553 */ red_transact(const char * pszVolume)554 int32_t red_transact( const char * pszVolume ) 555 { 556 REDSTATUS ret; 557 558 ret = PosixEnter(); 559 560 if( ret == 0 ) 561 { 562 uint8_t bVolNum; 563 564 ret = RedPathSplit( pszVolume, &bVolNum, NULL ); 565 566 #if REDCONF_VOLUME_COUNT > 1U 567 if( ret == 0 ) 568 { 569 ret = RedCoreVolSetCurrent( bVolNum ); 570 } 571 #endif 572 573 if( ret == 0 ) 574 { 575 ret = RedCoreVolTransact(); 576 } 577 578 PosixLeave(); 579 } 580 581 return PosixReturn( ret ); 582 } 583 #endif /* if REDCONF_READ_ONLY == 0 */ 584 585 586 #if REDCONF_READ_ONLY == 0 587 588 /** @brief Update the transaction mask. 589 * 590 * The following events are available: 591 * 592 * - #RED_TRANSACT_UMOUNT 593 * - #RED_TRANSACT_CREAT 594 * - #RED_TRANSACT_UNLINK 595 * - #RED_TRANSACT_MKDIR 596 * - #RED_TRANSACT_RENAME 597 * - #RED_TRANSACT_LINK 598 * - #RED_TRANSACT_CLOSE 599 * - #RED_TRANSACT_WRITE 600 * - #RED_TRANSACT_FSYNC 601 * - #RED_TRANSACT_TRUNCATE 602 * - #RED_TRANSACT_VOLFULL 603 * 604 * The #RED_TRANSACT_MANUAL macro (by itself) may be used to disable all 605 * automatic transaction events. The #RED_TRANSACT_MASK macro is a bitmask 606 * of all transaction flags, excluding those representing excluded 607 * functionality. 608 * 609 * Attempting to enable events for excluded functionality will result in an 610 * error. 611 * 612 * @param pszVolume The path prefix of the volume whose transaction mask is 613 * being changed. 614 * @param ulEventMask A bitwise-OR'd mask of automatic transaction events to 615 * be set as the current transaction mode. 616 * 617 * @return On success, zero is returned. On error, -1 is returned and 618 #red_errno is set appropriately. 619 * 620 * <b>Errno values</b> 621 * - #RED_EINVAL: Volume is not mounted; or @p pszVolume is `NULL`; or 622 * @p ulEventMask contains invalid bits. 623 * - #RED_ENOENT: @p pszVolume is not a valid volume path prefix. 624 * - #RED_EUSERS: Cannot become a file system user: too many users. 625 */ red_settransmask(const char * pszVolume,uint32_t ulEventMask)626 int32_t red_settransmask( const char * pszVolume, 627 uint32_t ulEventMask ) 628 { 629 REDSTATUS ret; 630 631 ret = PosixEnter(); 632 633 if( ret == 0 ) 634 { 635 uint8_t bVolNum; 636 637 ret = RedPathSplit( pszVolume, &bVolNum, NULL ); 638 639 #if REDCONF_VOLUME_COUNT > 1U 640 if( ret == 0 ) 641 { 642 ret = RedCoreVolSetCurrent( bVolNum ); 643 } 644 #endif 645 646 if( ret == 0 ) 647 { 648 ret = RedCoreTransMaskSet( ulEventMask ); 649 } 650 651 PosixLeave(); 652 } 653 654 return PosixReturn( ret ); 655 } 656 #endif /* if REDCONF_READ_ONLY == 0 */ 657 658 659 /** @brief Read the transaction mask. 660 * 661 * If the volume is read-only, the returned event mask is always zero. 662 * 663 * @param pszVolume The path prefix of the volume whose transaction mask is 664 * being retrieved. 665 * @param pulEventMask Populated with a bitwise-OR'd mask of automatic 666 * transaction events which represent the current 667 * transaction mode for the volume. 668 * 669 * @return On success, zero is returned. On error, -1 is returned and 670 #red_errno is set appropriately. 671 * 672 * <b>Errno values</b> 673 * - #RED_EINVAL: Volume is not mounted; or @p pszVolume is `NULL`; or 674 * @p pulEventMask is `NULL`. 675 * - #RED_ENOENT: @p pszVolume is not a valid volume path prefix. 676 * - #RED_EUSERS: Cannot become a file system user: too many users. 677 */ red_gettransmask(const char * pszVolume,uint32_t * pulEventMask)678 int32_t red_gettransmask( const char * pszVolume, 679 uint32_t * pulEventMask ) 680 { 681 REDSTATUS ret; 682 683 ret = PosixEnter(); 684 685 if( ret == 0 ) 686 { 687 uint8_t bVolNum; 688 689 ret = RedPathSplit( pszVolume, &bVolNum, NULL ); 690 691 #if REDCONF_VOLUME_COUNT > 1U 692 if( ret == 0 ) 693 { 694 ret = RedCoreVolSetCurrent( bVolNum ); 695 } 696 #endif 697 698 if( ret == 0 ) 699 { 700 ret = RedCoreTransMaskGet( pulEventMask ); 701 } 702 703 PosixLeave(); 704 } 705 706 return PosixReturn( ret ); 707 } 708 709 710 /** @brief Query file system status information. 711 * 712 * @p pszVolume should name a valid volume prefix or a valid root directory; 713 * this differs from POSIX statvfs, where any existing file or directory is a 714 * valid path. 715 * 716 * @param pszVolume The path prefix of the volume to query. 717 * @param pStatvfs The buffer to populate with volume information. 718 * 719 * @return On success, zero is returned. On error, -1 is returned and 720 #red_errno is set appropriately. 721 * 722 * <b>Errno values</b> 723 * - #RED_EINVAL: Volume is not mounted; or @p pszVolume is `NULL`; or 724 * @p pStatvfs is `NULL`. 725 * - #RED_ENOENT: @p pszVolume is not a valid volume path prefix. 726 * - #RED_EUSERS: Cannot become a file system user: too many users. 727 */ red_statvfs(const char * pszVolume,REDSTATFS * pStatvfs)728 int32_t red_statvfs( const char * pszVolume, 729 REDSTATFS * pStatvfs ) 730 { 731 REDSTATUS ret; 732 733 ret = PosixEnter(); 734 735 if( ret == 0 ) 736 { 737 uint8_t bVolNum; 738 739 ret = RedPathSplit( pszVolume, &bVolNum, NULL ); 740 741 #if REDCONF_VOLUME_COUNT > 1U 742 if( ret == 0 ) 743 { 744 ret = RedCoreVolSetCurrent( bVolNum ); 745 } 746 #endif 747 748 if( ret == 0 ) 749 { 750 ret = RedCoreVolStat( pStatvfs ); 751 } 752 753 PosixLeave(); 754 } 755 756 return PosixReturn( ret ); 757 } 758 759 760 /** @brief Open a file or directory. 761 * 762 * Exactly one file access mode must be specified: 763 * 764 * - #RED_O_RDONLY: Open for reading only. 765 * - #RED_O_WRONLY: Open for writing only. 766 * - #RED_O_RDWR: Open for reading and writing. 767 * 768 * Directories can only be opened with `RED_O_RDONLY`. 769 * 770 * The following flags may also be used: 771 * 772 * - #RED_O_APPEND: Set the file offset to the end-of-file prior to each 773 * write. 774 * - #RED_O_CREAT: Create the named file if it does not exist. 775 * - #RED_O_EXCL: In combination with `RED_O_CREAT`, return an error if the 776 * path already exists. 777 * - #RED_O_TRUNC: Truncate the opened file to size zero. Only supported when 778 #REDCONF_API_POSIX_FTRUNCATE is true. 779 * 780 #RED_O_CREAT, #RED_O_EXCL, and #RED_O_TRUNC are invalid with #RED_O_RDONLY. 781 #RED_O_EXCL is invalid without #RED_O_CREAT. 782 * 783 * If the volume is read-only, #RED_O_RDONLY is the only valid open flag; use 784 * of any other flag will result in an error. 785 * 786 * If #RED_O_TRUNC frees data which is in the committed state, it will not 787 * return to free space until after a transaction point. 788 * 789 * The returned file descriptor must later be closed with red_close(). 790 * 791 * Unlike POSIX open, there is no optional third argument for the permissions 792 * (which Reliance Edge does not use) and other open flags (like `O_SYNC`) are 793 * not supported. 794 * 795 * @param pszPath The path to the file or directory. 796 * @param ulOpenMode The open flags (mask of `RED_O_` values). 797 * 798 * @return On success, a nonnegative file descriptor is returned. On error, 799 * -1 is returned and #red_errno is set appropriately. 800 * 801 * <b>Errno values</b> 802 * - #RED_EEXIST: Using #RED_O_CREAT and #RED_O_EXCL, and the indicated path 803 * already exists. 804 * - #RED_EINVAL: @p ulOpenMode is invalid; or @p pszPath is `NULL`; or the 805 * volume containing the path is not mounted. 806 * - #RED_EIO: A disk I/O error occurred. 807 * - #RED_EISDIR: The path names a directory and @p ulOpenMode includes 808 #RED_O_WRONLY or #RED_O_RDWR. 809 * - #RED_EMFILE: There are no available file descriptors. 810 * - #RED_ENAMETOOLONG: The length of a component of @p pszPath is longer than 811 #REDCONF_NAME_MAX. 812 * - #RED_ENFILE: Attempting to create a file but the file system has used all 813 * available inode slots. 814 * - #RED_ENOENT: #RED_O_CREAT is not set and the named file does not exist; or 815 #RED_O_CREAT is set and the parent directory does not exist; or the 816 * volume does not exist; or the @p pszPath argument, after removing the 817 * volume prefix, points to an empty string. 818 * - #RED_ENOSPC: The file does not exist and #RED_O_CREAT was specified, but 819 * there is insufficient free space to expand the directory or to create the 820 * new file. 821 * - #RED_ENOTDIR: A component of the prefix in @p pszPath does not name a 822 * directory. 823 * - #RED_EROFS: The path resides on a read-only file system and a write 824 * operation was requested. 825 * - #RED_EUSERS: Cannot become a file system user: too many users. 826 */ red_open(const char * pszPath,uint32_t ulOpenMode)827 int32_t red_open( const char * pszPath, 828 uint32_t ulOpenMode ) 829 { 830 int32_t iFildes = -1; /* Init'd to quiet warnings. */ 831 REDSTATUS ret; 832 833 #if REDCONF_READ_ONLY == 1 834 if( ulOpenMode != RED_O_RDONLY ) 835 { 836 ret = -RED_EROFS; 837 } 838 #else 839 if( ( ulOpenMode != ( ulOpenMode & RED_O_MASK ) ) || 840 ( ( ulOpenMode & ( RED_O_RDONLY | RED_O_WRONLY | RED_O_RDWR ) ) == 0U ) || 841 ( ( ( ulOpenMode & RED_O_RDONLY ) != 0U ) && ( ( ulOpenMode & ( RED_O_WRONLY | RED_O_RDWR ) ) != 0U ) ) || 842 ( ( ( ulOpenMode & RED_O_WRONLY ) != 0U ) && ( ( ulOpenMode & ( RED_O_RDONLY | RED_O_RDWR ) ) != 0U ) ) || 843 ( ( ( ulOpenMode & RED_O_RDWR ) != 0U ) && ( ( ulOpenMode & ( RED_O_RDONLY | RED_O_WRONLY ) ) != 0U ) ) || 844 ( ( ( ulOpenMode & ( RED_O_TRUNC | RED_O_CREAT | RED_O_EXCL ) ) != 0U ) && ( ( ulOpenMode & RED_O_RDONLY ) != 0U ) ) || 845 ( ( ( ulOpenMode & RED_O_EXCL ) != 0U ) && ( ( ulOpenMode & RED_O_CREAT ) == 0U ) ) ) 846 { 847 ret = -RED_EINVAL; 848 } 849 850 #if REDCONF_API_POSIX_FTRUNCATE == 0 851 else if( ( ulOpenMode & RED_O_TRUNC ) != 0U ) 852 { 853 ret = -RED_EINVAL; 854 } 855 #endif 856 #endif /* if REDCONF_READ_ONLY == 1 */ 857 else 858 { 859 ret = PosixEnter(); 860 } 861 862 if( ret == 0 ) 863 { 864 ret = FildesOpen( pszPath, ulOpenMode, FTYPE_EITHER, &iFildes ); 865 866 PosixLeave(); 867 } 868 869 if( ret != 0 ) 870 { 871 iFildes = PosixReturn( ret ); 872 } 873 874 return iFildes; 875 } 876 877 878 #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX_UNLINK == 1 ) 879 880 /** @brief Delete a file or directory. 881 * 882 * The given name is deleted and the link count of the corresponding inode is 883 * decremented. If the link count falls to zero (no remaining hard links), 884 * the inode will be deleted. 885 * 886 * Unlike POSIX unlink, deleting a file or directory with open handles (file 887 * descriptors or directory streams) will fail with an #RED_EBUSY error. This 888 * only applies when deleting an inode with a link count of one; if a file has 889 * multiple names (hard links), all but the last name may be deleted even if 890 * the file is open. 891 * 892 * If the path names a directory which is not empty, the unlink will fail. 893 * 894 * If the deletion frees data in the committed state, it will not return to 895 * free space until after a transaction point. 896 * 897 * Unlike POSIX unlink, this function can fail when the disk is full. To fix 898 * this, transact and try again: Reliance Edge guarantees that it is possible 899 * to delete at least one file or directory after a transaction point. If disk 900 * full automatic transactions are enabled, this will happen automatically. 901 * 902 * @param pszPath The path of the file or directory to delete. 903 * 904 * @return On success, zero is returned. On error, -1 is returned and 905 #red_errno is set appropriately. 906 * 907 * <b>Errno values</b> 908 * - #RED_EBUSY: @p pszPath points to an inode with open handles and a link 909 * count of one. 910 * - #RED_EINVAL: @p pszPath is `NULL`; or the volume containing the path is 911 * not mounted. 912 * - #RED_EIO: A disk I/O error occurred. 913 * - #RED_ENAMETOOLONG: The length of a component of @p pszPath is longer than 914 #REDCONF_NAME_MAX. 915 * - #RED_ENOENT: The path does not name an existing file; or the @p pszPath 916 * argument, after removing the volume prefix, points to an empty string. 917 * - #RED_ENOTDIR: A component of the path prefix is not a directory. 918 * - #RED_ENOTEMPTY: The path names a directory which is not empty. 919 * - #RED_ENOSPC: The file system does not have enough space to modify the 920 * parent directory to perform the deletion. 921 * - #RED_EUSERS: Cannot become a file system user: too many users. 922 */ red_unlink(const char * pszPath)923 int32_t red_unlink( const char * pszPath ) 924 { 925 REDSTATUS ret; 926 927 ret = PosixEnter(); 928 929 if( ret == 0 ) 930 { 931 ret = UnlinkSub( pszPath, FTYPE_EITHER ); 932 933 PosixLeave(); 934 } 935 936 return PosixReturn( ret ); 937 } 938 #endif /* if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX_UNLINK == 1 ) */ 939 940 941 #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX_MKDIR == 1 ) 942 943 /** @brief Create a new directory. 944 * 945 * Unlike POSIX mkdir, this function has no second argument for the 946 * permissions (which Reliance Edge does not use). 947 * 948 * @param pszPath The name and location of the directory to create. 949 * 950 * @return On success, zero is returned. On error, -1 is returned and 951 #red_errno is set appropriately. 952 * 953 * <b>Errno values</b> 954 * - #RED_EEXIST: @p pszPath points to an existing file or directory. 955 * - #RED_EINVAL: @p pszPath is `NULL`; or the volume containing the path is 956 * not mounted. 957 * - #RED_EIO: A disk I/O error occurred. 958 * - #RED_ENAMETOOLONG: The length of a component of @p pszPath is longer than 959 #REDCONF_NAME_MAX. 960 * - #RED_ENOENT: A component of the path prefix does not name an existing 961 * directory; or the @p pszPath argument, after removing the volume prefix, 962 * points to an empty string. 963 * - #RED_ENOSPC: The file system does not have enough space for the new 964 * directory or to extend the parent directory of the new directory. 965 * - #RED_ENOTDIR: A component of the path prefix is not a directory. 966 * - #RED_EROFS: The parent directory resides on a read-only file system. 967 * - #RED_EUSERS: Cannot become a file system user: too many users. 968 */ red_mkdir(const char * pszPath)969 int32_t red_mkdir( const char * pszPath ) 970 { 971 REDSTATUS ret; 972 973 ret = PosixEnter(); 974 975 if( ret == 0 ) 976 { 977 const char * pszLocalPath; 978 uint8_t bVolNum; 979 980 ret = RedPathSplit( pszPath, &bVolNum, &pszLocalPath ); 981 982 #if REDCONF_VOLUME_COUNT > 1U 983 if( ret == 0 ) 984 { 985 ret = RedCoreVolSetCurrent( bVolNum ); 986 } 987 #endif 988 989 if( ret == 0 ) 990 { 991 const char * pszName; 992 uint32_t ulPInode; 993 994 ret = RedPathToName( pszLocalPath, &ulPInode, &pszName ); 995 996 if( ret == 0 ) 997 { 998 uint32_t ulInode; 999 1000 ret = RedCoreCreate( ulPInode, pszName, true, &ulInode ); 1001 } 1002 } 1003 1004 PosixLeave(); 1005 } 1006 1007 return PosixReturn( ret ); 1008 } 1009 #endif /* if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX_MKDIR == 1 ) */ 1010 1011 1012 #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX_RMDIR == 1 ) 1013 1014 /** @brief Delete a directory. 1015 * 1016 * The given directory name is deleted and the corresponding directory inode 1017 * will be deleted. 1018 * 1019 * Unlike POSIX rmdir, deleting a directory with open handles (file 1020 * descriptors or directory streams) will fail with an #RED_EBUSY error. 1021 * 1022 * If the path names a directory which is not empty, the deletion will fail. 1023 * If the path names the root directory of a file system volume, the deletion 1024 * will fail. 1025 * 1026 * If the path names a regular file, the deletion will fail. This provides 1027 * type checking and may be useful in cases where an application knows the 1028 * path to be deleted should name a directory. 1029 * 1030 * If the deletion frees data in the committed state, it will not return to 1031 * free space until after a transaction point. 1032 * 1033 * Unlike POSIX rmdir, this function can fail when the disk is full. To fix 1034 * this, transact and try again: Reliance Edge guarantees that it is possible 1035 * to delete at least one file or directory after a transaction point. If disk 1036 * full automatic transactions are enabled, this will happen automatically. 1037 * 1038 * @param pszPath The path of the directory to delete. 1039 * 1040 * @return On success, zero is returned. On error, -1 is returned and 1041 #red_errno is set appropriately. 1042 * 1043 * <b>Errno values</b> 1044 * - #RED_EBUSY: @p pszPath points to a directory with open handles. 1045 * - #RED_EINVAL: @p pszPath is `NULL`; or the volume containing the path is 1046 * not mounted. 1047 * - #RED_EIO: A disk I/O error occurred. 1048 * - #RED_ENAMETOOLONG: The length of a component of @p pszPath is longer than 1049 #REDCONF_NAME_MAX. 1050 * - #RED_ENOENT: The path does not name an existing directory; or the 1051 * @p pszPath argument, after removing the volume prefix, points to an empty 1052 * string. 1053 * - #RED_ENOTDIR: A component of the path is not a directory. 1054 * - #RED_ENOTEMPTY: The path names a directory which is not empty. 1055 * - #RED_ENOSPC: The file system does not have enough space to modify the 1056 * parent directory to perform the deletion. 1057 * - #RED_EROFS: The directory to be removed resides on a read-only file 1058 * system. 1059 * - #RED_EUSERS: Cannot become a file system user: too many users. 1060 */ red_rmdir(const char * pszPath)1061 int32_t red_rmdir( const char * pszPath ) 1062 { 1063 REDSTATUS ret; 1064 1065 ret = PosixEnter(); 1066 1067 if( ret == 0 ) 1068 { 1069 ret = UnlinkSub( pszPath, FTYPE_DIR ); 1070 1071 PosixLeave(); 1072 } 1073 1074 return PosixReturn( ret ); 1075 } 1076 #endif /* if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX_RMDIR == 1 ) */ 1077 1078 1079 #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX_RENAME == 1 ) 1080 1081 /** @brief Rename a file or directory. 1082 * 1083 * Both paths must reside on the same file system volume. Attempting to use 1084 * this API to move a file to a different volume will result in an error. 1085 * 1086 * If @p pszNewPath names an existing file or directory, the behavior depends 1087 * on the configuration. If #REDCONF_RENAME_ATOMIC is false, and if the 1088 * destination name exists, this function always fails and sets #red_errno to 1089 #RED_EEXIST. This behavior is contrary to POSIX. 1090 * 1091 * If #REDCONF_RENAME_ATOMIC is true, and if the new name exists, then in one 1092 * atomic operation, @p pszNewPath is unlinked and @p pszOldPath is renamed to 1093 * @p pszNewPath. Both @p pszNewPath and @p pszOldPath must be of the same 1094 * type (both files or both directories). As with red_unlink(), if 1095 * @p pszNewPath is a directory, it must be empty. The major exception to this 1096 * behavior is that if both @p pszOldPath and @p pszNewPath are links to the 1097 * same inode, then the rename does nothing and both names continue to exist. 1098 * Unlike POSIX rename, if @p pszNewPath points to an inode with a link count 1099 * of one and open handles (file descriptors or directory streams), the 1100 * rename will fail with #RED_EBUSY. 1101 * 1102 * If the rename deletes the old destination, it may free data in the 1103 * committed state, which will not return to free space until after a 1104 * transaction point. Similarly, if the deleted inode was part of the 1105 * committed state, the inode slot will not be available until after a 1106 * transaction point. 1107 * 1108 * @param pszOldPath The path of the file or directory to rename. 1109 * @param pszNewPath The new name and location after the rename. 1110 * 1111 * @return On success, zero is returned. On error, -1 is returned and 1112 #red_errno is set appropriately. 1113 * 1114 * <b>Errno values</b> 1115 * - #RED_EBUSY: #REDCONF_RENAME_ATOMIC is true and @p pszNewPath points to an 1116 * inode with open handles and a link count of one. 1117 * - #RED_EEXIST: #REDCONF_RENAME_ATOMIC is false and @p pszNewPath exists. 1118 * - #RED_EINVAL: @p pszOldPath is `NULL`; or @p pszNewPath is `NULL`; or the 1119 * volume containing the path is not mounted. 1120 * - #RED_EIO: A disk I/O error occurred. 1121 * - #RED_EISDIR: The @p pszNewPath argument names a directory and the 1122 * @p pszOldPath argument names a non-directory. 1123 * - #RED_ENAMETOOLONG: The length of a component of either @p pszOldPath or 1124 * @p pszNewPath is longer than #REDCONF_NAME_MAX. 1125 * - #RED_ENOENT: The link named by @p pszOldPath does not name an existing 1126 * entry; or either @p pszOldPath or @p pszNewPath, after removing the volume 1127 * prefix, point to an empty string. 1128 * - #RED_ENOTDIR: A component of either path prefix is not a directory; or 1129 * @p pszOldPath names a directory and @p pszNewPath names a file. 1130 * - #RED_ENOTEMPTY: The path named by @p pszNewPath is a directory which is 1131 * not empty. 1132 * - #RED_ENOSPC: The file system does not have enough space to extend the 1133 * directory that would contain @p pszNewPath. 1134 * - #RED_EROFS: The directory to be removed resides on a read-only file 1135 * system. 1136 * - #RED_EUSERS: Cannot become a file system user: too many users. 1137 * - #RED_EXDEV: @p pszOldPath and @p pszNewPath are on different file system 1138 * volumes. 1139 */ red_rename(const char * pszOldPath,const char * pszNewPath)1140 int32_t red_rename( const char * pszOldPath, 1141 const char * pszNewPath ) 1142 { 1143 REDSTATUS ret; 1144 1145 ret = PosixEnter(); 1146 1147 if( ret == 0 ) 1148 { 1149 const char * pszOldLocalPath; 1150 uint8_t bOldVolNum; 1151 1152 ret = RedPathSplit( pszOldPath, &bOldVolNum, &pszOldLocalPath ); 1153 1154 if( ret == 0 ) 1155 { 1156 const char * pszNewLocalPath; 1157 uint8_t bNewVolNum; 1158 1159 ret = RedPathSplit( pszNewPath, &bNewVolNum, &pszNewLocalPath ); 1160 1161 if( ( ret == 0 ) && ( bOldVolNum != bNewVolNum ) ) 1162 { 1163 ret = -RED_EXDEV; 1164 } 1165 1166 #if REDCONF_VOLUME_COUNT > 1U 1167 if( ret == 0 ) 1168 { 1169 ret = RedCoreVolSetCurrent( bOldVolNum ); 1170 } 1171 #endif 1172 1173 if( ret == 0 ) 1174 { 1175 const char * pszOldName; 1176 uint32_t ulOldPInode; 1177 1178 ret = RedPathToName( pszOldLocalPath, &ulOldPInode, &pszOldName ); 1179 1180 if( ret == 0 ) 1181 { 1182 const char * pszNewName; 1183 uint32_t ulNewPInode; 1184 1185 ret = RedPathToName( pszNewLocalPath, &ulNewPInode, &pszNewName ); 1186 1187 #if REDCONF_RENAME_ATOMIC == 1 1188 if( ret == 0 ) 1189 { 1190 uint32_t ulDestInode; 1191 1192 ret = RedCoreLookup( ulNewPInode, pszNewName, &ulDestInode ); 1193 1194 if( ret == 0 ) 1195 { 1196 ret = InodeUnlinkCheck( ulDestInode ); 1197 } 1198 else if( ret == -RED_ENOENT ) 1199 { 1200 ret = 0; 1201 } 1202 else 1203 { 1204 /* Unexpected error, nothing to do. 1205 */ 1206 } 1207 } 1208 #endif /* if REDCONF_RENAME_ATOMIC == 1 */ 1209 1210 if( ret == 0 ) 1211 { 1212 ret = RedCoreRename( ulOldPInode, pszOldName, ulNewPInode, pszNewName ); 1213 } 1214 } 1215 } 1216 } 1217 1218 PosixLeave(); 1219 } 1220 1221 return PosixReturn( ret ); 1222 } 1223 #endif /* if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX_RENAME == 1 ) */ 1224 1225 1226 #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX_LINK == 1 ) 1227 1228 /** @brief Create a hard link. 1229 * 1230 * This creates an additional name (link) for the file named by @p pszPath. 1231 * The new name refers to the same file with the same contents. If a name is 1232 * deleted, but the underlying file has other names, the file continues to 1233 * exist. The link count (accessible via red_fstat()) indicates the number of 1234 * names that a file has. All of a file's names are on equal footing: there 1235 * is nothing special about the original name. 1236 * 1237 * If @p pszPath names a directory, the operation will fail. 1238 * 1239 * @param pszPath The path indicating the inode for the new link. 1240 * @param pszHardLink The name and location for the new link. 1241 * 1242 * @return On success, zero is returned. On error, -1 is returned and 1243 #red_errno is set appropriately. 1244 * 1245 * <b>Errno values</b> 1246 * - #RED_EEXIST: @p pszHardLink resolves to an existing file. 1247 * - #RED_EINVAL: @p pszPath or @p pszHardLink is `NULL`; or the volume 1248 * containing the paths is not mounted. 1249 * - #RED_EIO: A disk I/O error occurred. 1250 * - #RED_EMLINK: Creating the link would exceed the maximum link count of the 1251 * inode named by @p pszPath. 1252 * - #RED_ENAMETOOLONG: The length of a component of either @p pszPath or 1253 * @p pszHardLink is longer than #REDCONF_NAME_MAX. 1254 * - #RED_ENOENT: A component of either path prefix does not exist; or the file 1255 * named by @p pszPath does not exist; or either @p pszPath or 1256 * @p pszHardLink, after removing the volume prefix, point to an empty 1257 * string. 1258 * - #RED_ENOSPC: There is insufficient free space to expand the directory that 1259 * would contain the link. 1260 * - #RED_ENOTDIR: A component of either path prefix is not a directory. 1261 * - #RED_EPERM: The @p pszPath argument names a directory. 1262 * - #RED_EROFS: The requested link requires writing in a directory on a 1263 * read-only file system. 1264 * - #RED_EUSERS: Cannot become a file system user: too many users. 1265 * - #RED_EXDEV: @p pszPath and @p pszHardLink are on different file system 1266 * volumes. 1267 */ red_link(const char * pszPath,const char * pszHardLink)1268 int32_t red_link( const char * pszPath, 1269 const char * pszHardLink ) 1270 { 1271 REDSTATUS ret; 1272 1273 ret = PosixEnter(); 1274 1275 if( ret == 0 ) 1276 { 1277 const char * pszLocalPath; 1278 uint8_t bVolNum; 1279 1280 ret = RedPathSplit( pszPath, &bVolNum, &pszLocalPath ); 1281 1282 if( ret == 0 ) 1283 { 1284 const char * pszLinkLocalPath; 1285 uint8_t bLinkVolNum; 1286 1287 ret = RedPathSplit( pszHardLink, &bLinkVolNum, &pszLinkLocalPath ); 1288 1289 if( ( ret == 0 ) && ( bVolNum != bLinkVolNum ) ) 1290 { 1291 ret = -RED_EXDEV; 1292 } 1293 1294 #if REDCONF_VOLUME_COUNT > 1U 1295 if( ret == 0 ) 1296 { 1297 ret = RedCoreVolSetCurrent( bVolNum ); 1298 } 1299 #endif 1300 1301 if( ret == 0 ) 1302 { 1303 uint32_t ulInode; 1304 1305 ret = RedPathLookup( pszLocalPath, &ulInode ); 1306 1307 if( ret == 0 ) 1308 { 1309 const char * pszLinkName; 1310 uint32_t ulLinkPInode; 1311 1312 ret = RedPathToName( pszLinkLocalPath, &ulLinkPInode, &pszLinkName ); 1313 1314 if( ret == 0 ) 1315 { 1316 ret = RedCoreLink( ulLinkPInode, pszLinkName, ulInode ); 1317 } 1318 } 1319 } 1320 } 1321 1322 PosixLeave(); 1323 } 1324 1325 return PosixReturn( ret ); 1326 } 1327 #endif /* if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX_LINK == 1 ) */ 1328 1329 1330 /** @brief Close a file descriptor. 1331 * 1332 * @param iFildes The file descriptor to close. 1333 * 1334 * @return On success, zero is returned. On error, -1 is returned and 1335 #red_errno is set appropriately. 1336 * 1337 * <b>Errno values</b> 1338 * - #RED_EBADF: @p iFildes is not a valid file descriptor. 1339 * - #RED_EIO: A disk I/O error occurred. 1340 * - #RED_EUSERS: Cannot become a file system user: too many users. 1341 */ red_close(int32_t iFildes)1342 int32_t red_close( int32_t iFildes ) 1343 { 1344 REDSTATUS ret; 1345 1346 ret = PosixEnter(); 1347 1348 if( ret == 0 ) 1349 { 1350 ret = FildesClose( iFildes ); 1351 1352 PosixLeave(); 1353 } 1354 1355 return PosixReturn( ret ); 1356 } 1357 1358 1359 /** @brief Read from an open file. 1360 * 1361 * The read takes place at the file offset associated with @p iFildes and 1362 * advances the file offset by the number of bytes actually read. 1363 * 1364 * Data which has not yet been written, but which is before the end-of-file 1365 * (sparse data), will read as zeroes. A short read -- where the number of 1366 * bytes read is less than requested -- indicates that the requested read was 1367 * partially or, if zero bytes were read, entirely beyond the end-of-file. 1368 * 1369 * @param iFildes The file descriptor from which to read. 1370 * @param pBuffer The buffer to populate with data read. Must be at least 1371 * @p ulLength bytes in size. 1372 * @param ulLength Number of bytes to attempt to read. 1373 * 1374 * @return On success, returns a nonnegative value indicating the number of 1375 * bytes actually read. On error, -1 is returned and #red_errno is 1376 * set appropriately. 1377 * 1378 * <b>Errno values</b> 1379 * - #RED_EBADF: The @p iFildes argument is not a valid file descriptor open 1380 * for reading. 1381 * - #RED_EINVAL: @p pBuffer is `NULL`; or @p ulLength exceeds INT32_MAX and 1382 * cannot be returned properly. 1383 * - #RED_EIO: A disk I/O error occurred. 1384 * - #RED_EISDIR: The @p iFildes is a file descriptor for a directory. 1385 * - #RED_EUSERS: Cannot become a file system user: too many users. 1386 */ red_read(int32_t iFildes,void * pBuffer,uint32_t ulLength)1387 int32_t red_read( int32_t iFildes, 1388 void * pBuffer, 1389 uint32_t ulLength ) 1390 { 1391 uint32_t ulLenRead = 0U; 1392 REDSTATUS ret; 1393 int32_t iReturn; 1394 1395 if( ulLength > ( uint32_t ) INT32_MAX ) 1396 { 1397 ret = -RED_EINVAL; 1398 } 1399 else 1400 { 1401 ret = PosixEnter(); 1402 } 1403 1404 if( ret == 0 ) 1405 { 1406 REDHANDLE * pHandle; 1407 1408 ret = FildesToHandle( iFildes, FTYPE_FILE, &pHandle ); 1409 1410 if( ( ret == 0 ) && ( ( pHandle->bFlags & HFLAG_READABLE ) == 0U ) ) 1411 { 1412 ret = -RED_EBADF; 1413 } 1414 1415 #if REDCONF_VOLUME_COUNT > 1U 1416 if( ret == 0 ) 1417 { 1418 ret = RedCoreVolSetCurrent( pHandle->bVolNum ); 1419 } 1420 #endif 1421 1422 if( ret == 0 ) 1423 { 1424 ulLenRead = ulLength; 1425 ret = RedCoreFileRead( pHandle->ulInode, pHandle->ullOffset, &ulLenRead, pBuffer ); 1426 } 1427 1428 if( ret == 0 ) 1429 { 1430 REDASSERT( ulLenRead <= ulLength ); 1431 1432 pHandle->ullOffset += ulLenRead; 1433 } 1434 1435 PosixLeave(); 1436 } 1437 1438 if( ret == 0 ) 1439 { 1440 iReturn = ( int32_t ) ulLenRead; 1441 } 1442 else 1443 { 1444 iReturn = PosixReturn( ret ); 1445 } 1446 1447 return iReturn; 1448 } 1449 1450 1451 #if REDCONF_READ_ONLY == 0 1452 1453 /** @brief Write to an open file. 1454 * 1455 * The write takes place at the file offset associated with @p iFildes and 1456 * advances the file offset by the number of bytes actually written. 1457 * Alternatively, if @p iFildes was opened with #RED_O_APPEND, the file offset 1458 * is set to the end-of-file before the write begins, and likewise advances by 1459 * the number of bytes actually written. 1460 * 1461 * A short write -- where the number of bytes written is less than requested 1462 * -- indicates either that the file system ran out of space but was still 1463 * able to write some of the request; or that the request would have caused 1464 * the file to exceed the maximum file size, but some of the data could be 1465 * written prior to the file size limit. 1466 * 1467 * If an error is returned (-1), either none of the data was written or a 1468 * critical error occurred (like an I/O error) and the file system volume will 1469 * be read-only. 1470 * 1471 * @param iFildes The file descriptor to write to. 1472 * @param pBuffer The buffer containing the data to be written. Must be at 1473 * least @p ulLength bytes in size. 1474 * @param ulLength Number of bytes to attempt to write. 1475 * 1476 * @return On success, returns a nonnegative value indicating the number of 1477 * bytes actually written. On error, -1 is returned and #red_errno is 1478 * set appropriately. 1479 * 1480 * <b>Errno values</b> 1481 * - #RED_EBADF: The @p iFildes argument is not a valid file descriptor open 1482 * for writing. This includes the case where the file descriptor is for a 1483 * directory. 1484 * - #RED_EFBIG: No data can be written to the current file offset since the 1485 * resulting file size would exceed the maximum file size. 1486 * - #RED_EINVAL: @p pBuffer is `NULL`; or @p ulLength exceeds INT32_MAX and 1487 * cannot be returned properly. 1488 * - #RED_EIO: A disk I/O error occurred. 1489 * - #RED_ENOSPC: No data can be written because there is insufficient free 1490 * space. 1491 * - #RED_EUSERS: Cannot become a file system user: too many users. 1492 */ red_write(int32_t iFildes,const void * pBuffer,uint32_t ulLength)1493 int32_t red_write( int32_t iFildes, 1494 const void * pBuffer, 1495 uint32_t ulLength ) 1496 { 1497 uint32_t ulLenWrote = 0U; 1498 REDSTATUS ret; 1499 int32_t iReturn; 1500 1501 if( ulLength > ( uint32_t ) INT32_MAX ) 1502 { 1503 ret = -RED_EINVAL; 1504 } 1505 else 1506 { 1507 ret = PosixEnter(); 1508 } 1509 1510 if( ret == 0 ) 1511 { 1512 REDHANDLE * pHandle; 1513 1514 ret = FildesToHandle( iFildes, FTYPE_FILE, &pHandle ); 1515 1516 if( ret == -RED_EISDIR ) 1517 { 1518 /* POSIX says that if a file descriptor is not writable, the 1519 * errno should be -RED_EBADF. Directory file descriptors are 1520 * never writable, and unlike for read(), the spec does not 1521 * list -RED_EISDIR as an allowed errno. Therefore -RED_EBADF 1522 * takes precedence. 1523 */ 1524 ret = -RED_EBADF; 1525 } 1526 1527 if( ( ret == 0 ) && ( ( pHandle->bFlags & HFLAG_WRITEABLE ) == 0U ) ) 1528 { 1529 ret = -RED_EBADF; 1530 } 1531 1532 #if REDCONF_VOLUME_COUNT > 1U 1533 if( ret == 0 ) 1534 { 1535 ret = RedCoreVolSetCurrent( pHandle->bVolNum ); 1536 } 1537 #endif 1538 1539 if( ( ret == 0 ) && ( ( pHandle->bFlags & HFLAG_APPENDING ) != 0U ) ) 1540 { 1541 REDSTAT s; 1542 1543 ret = RedCoreStat( pHandle->ulInode, &s ); 1544 1545 if( ret == 0 ) 1546 { 1547 pHandle->ullOffset = s.st_size; 1548 } 1549 } 1550 1551 if( ret == 0 ) 1552 { 1553 ulLenWrote = ulLength; 1554 ret = RedCoreFileWrite( pHandle->ulInode, pHandle->ullOffset, &ulLenWrote, pBuffer ); 1555 } 1556 1557 if( ret == 0 ) 1558 { 1559 REDASSERT( ulLenWrote <= ulLength ); 1560 1561 pHandle->ullOffset += ulLenWrote; 1562 } 1563 1564 PosixLeave(); 1565 } 1566 1567 if( ret == 0 ) 1568 { 1569 iReturn = ( int32_t ) ulLenWrote; 1570 } 1571 else 1572 { 1573 iReturn = PosixReturn( ret ); 1574 } 1575 1576 return iReturn; 1577 } 1578 #endif /* if REDCONF_READ_ONLY == 0 */ 1579 1580 1581 #if REDCONF_READ_ONLY == 0 1582 1583 /** @brief Synchronizes changes to a file. 1584 * 1585 * Commits all changes associated with a file or directory (including file 1586 * data, directory contents, and metadata) to permanent storage. This 1587 * function will not return until the operation is complete. 1588 * 1589 * In the current implementation, this function has global effect. All dirty 1590 * buffers are flushed and a transaction point is committed. Fsyncing one 1591 * file effectively fsyncs all files. 1592 * 1593 * If fsync automatic transactions have been disabled, this function does 1594 * nothing and returns success. In the current implementation, this is the 1595 * only real difference between this function and red_transact(): this 1596 * function can be configured to do nothing, whereas red_transact() is 1597 * unconditional. 1598 * 1599 * Applications written for portability should avoid assuming red_fsync() 1600 * effects all files, and use red_fsync() on each file that needs to be 1601 * synchronized. 1602 * 1603 * Passing read-only file descriptors to this function is permitted. 1604 * 1605 * @param iFildes The file descriptor to synchronize. 1606 * 1607 * @return On success, zero is returned. On error, -1 is returned and 1608 #red_errno is set appropriately. 1609 * 1610 * <b>Errno values</b> 1611 * - #RED_EBADF: The @p iFildes argument is not a valid file descriptor. 1612 * - #RED_EIO: A disk I/O error occurred. 1613 * - #RED_EUSERS: Cannot become a file system user: too many users. 1614 */ red_fsync(int32_t iFildes)1615 int32_t red_fsync( int32_t iFildes ) 1616 { 1617 REDSTATUS ret; 1618 1619 ret = PosixEnter(); 1620 1621 if( ret == 0 ) 1622 { 1623 REDHANDLE * pHandle; 1624 1625 ret = FildesToHandle( iFildes, FTYPE_EITHER, &pHandle ); 1626 1627 #if REDCONF_VOLUME_COUNT > 1U 1628 if( ret == 0 ) 1629 { 1630 ret = RedCoreVolSetCurrent( pHandle->bVolNum ); 1631 } 1632 #endif 1633 1634 /* No core event for fsync, so this transaction flag needs to be 1635 * implemented here. 1636 */ 1637 if( ret == 0 ) 1638 { 1639 uint32_t ulTransMask; 1640 1641 ret = RedCoreTransMaskGet( &ulTransMask ); 1642 1643 if( ( ret == 0 ) && ( ( ulTransMask & RED_TRANSACT_FSYNC ) != 0U ) ) 1644 { 1645 ret = RedCoreVolTransact(); 1646 } 1647 } 1648 1649 PosixLeave(); 1650 } 1651 1652 return PosixReturn( ret ); 1653 } 1654 #endif /* if REDCONF_READ_ONLY == 0 */ 1655 1656 1657 /** @brief Move the read/write file offset. 1658 * 1659 * The file offset of the @p iFildes file descriptor is set to @p llOffset, 1660 * relative to some starting position. The available positions are: 1661 * 1662 * - ::RED_SEEK_SET Seek from the start of the file. In other words, 1663 * @p llOffset becomes the new file offset. 1664 * - ::RED_SEEK_CUR Seek from the current file offset. In other words, 1665 * @p llOffset is added to the current file offset. 1666 * - ::RED_SEEK_END Seek from the end-of-file. In other words, the new file 1667 * offset is the file size plus @p llOffset. 1668 * 1669 * Since @p llOffset is signed (can be negative), it is possible to seek 1670 * backward with ::RED_SEEK_CUR or ::RED_SEEK_END. 1671 * 1672 * It is permitted to seek beyond the end-of-file; this does not increase the 1673 * file size (a subsequent red_write() call would). 1674 * 1675 * Unlike POSIX lseek, this function cannot be used with directory file 1676 * descriptors. 1677 * 1678 * @param iFildes The file descriptor whose offset is to be updated. 1679 * @param llOffset The new file offset, relative to @p whence. 1680 * @param whence The location from which @p llOffset should be applied. 1681 * 1682 * @return On success, returns the new file position, measured in bytes from 1683 * the beginning of the file. On error, -1 is returned and #red_errno 1684 * is set appropriately. 1685 * 1686 * <b>Errno values</b> 1687 * - #RED_EBADF: The @p iFildes argument is not an open file descriptor. 1688 * - #RED_EINVAL: @p whence is not a valid `RED_SEEK_` value; or the resulting 1689 * file offset would be negative or beyond the maximum file size. 1690 * - #RED_EIO: A disk I/O error occurred. 1691 * - #RED_EISDIR: The @p iFildes argument is a file descriptor for a directory. 1692 * - #RED_EUSERS: Cannot become a file system user: too many users. 1693 */ red_lseek(int32_t iFildes,int64_t llOffset,REDWHENCE whence)1694 int64_t red_lseek( int32_t iFildes, 1695 int64_t llOffset, 1696 REDWHENCE whence ) 1697 { 1698 REDSTATUS ret; 1699 int64_t llReturn = -1; /* Init'd to quiet warnings. */ 1700 1701 ret = PosixEnter(); 1702 1703 if( ret == 0 ) 1704 { 1705 int64_t llFrom = 0; /* Init'd to quiet warnings. */ 1706 REDHANDLE * pHandle; 1707 1708 /* Unlike POSIX, we disallow lseek() on directory handles. 1709 */ 1710 ret = FildesToHandle( iFildes, FTYPE_FILE, &pHandle ); 1711 1712 #if REDCONF_VOLUME_COUNT > 1U 1713 if( ret == 0 ) 1714 { 1715 ret = RedCoreVolSetCurrent( pHandle->bVolNum ); 1716 } 1717 #endif 1718 1719 if( ret == 0 ) 1720 { 1721 switch( whence ) 1722 { 1723 /* Seek from the beginning of the file. 1724 */ 1725 case RED_SEEK_SET: 1726 llFrom = 0; 1727 break; 1728 1729 /* Seek from the current file offset. 1730 */ 1731 case RED_SEEK_CUR: 1732 REDASSERT( pHandle->ullOffset <= ( uint64_t ) INT64_MAX ); 1733 llFrom = ( int64_t ) pHandle->ullOffset; 1734 break; 1735 1736 /* Seek from the end of the file. 1737 */ 1738 case RED_SEEK_END: 1739 { 1740 REDSTAT s; 1741 1742 ret = RedCoreStat( pHandle->ulInode, &s ); 1743 1744 if( ret == 0 ) 1745 { 1746 REDASSERT( s.st_size <= ( uint64_t ) INT64_MAX ); 1747 llFrom = ( int64_t ) s.st_size; 1748 } 1749 1750 break; 1751 } 1752 1753 default: 1754 ret = -RED_EINVAL; 1755 break; 1756 } 1757 } 1758 1759 if( ret == 0 ) 1760 { 1761 REDASSERT( llFrom >= 0 ); 1762 1763 /* Avoid signed integer overflow from llFrom + llOffset with large 1764 * values of llOffset and nonzero llFrom values. Underflow isn't 1765 * possible since llFrom is nonnegative. 1766 */ 1767 if( ( llOffset > 0 ) && ( ( ( uint64_t ) llFrom + ( uint64_t ) llOffset ) > ( uint64_t ) INT64_MAX ) ) 1768 { 1769 ret = -RED_EINVAL; 1770 } 1771 else 1772 { 1773 int64_t llNewOffset = llFrom + llOffset; 1774 1775 if( ( llNewOffset < 0 ) || ( ( uint64_t ) llNewOffset > gpRedVolume->ullMaxInodeSize ) ) 1776 { 1777 /* Invalid file offset. 1778 */ 1779 ret = -RED_EINVAL; 1780 } 1781 else 1782 { 1783 pHandle->ullOffset = ( uint64_t ) llNewOffset; 1784 llReturn = llNewOffset; 1785 } 1786 } 1787 } 1788 1789 PosixLeave(); 1790 } 1791 1792 if( ret != 0 ) 1793 { 1794 llReturn = PosixReturn( ret ); 1795 } 1796 1797 return llReturn; 1798 } 1799 1800 1801 #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX_FTRUNCATE == 1 ) 1802 1803 /** @brief Truncate a file to a specified length. 1804 * 1805 * Allows the file size to be increased, decreased, or to remain the same. If 1806 * the file size is increased, the new area is sparse (will read as zeroes). 1807 * If the file size is decreased, the data beyond the new end-of-file will 1808 * return to free space once it is no longer part of the committed state 1809 * (either immediately or after the next transaction point). 1810 * 1811 * The value of the file offset is not modified by this function. 1812 * 1813 * Unlike POSIX ftruncate, this function can fail when the disk is full if 1814 * @p ullSize is non-zero. If decreasing the file size, this can be fixed by 1815 * transacting and trying again: Reliance Edge guarantees that it is possible 1816 * to perform a truncate of at least one file that decreases the file size 1817 * after a transaction point. If disk full transactions are enabled, this will 1818 * happen automatically. 1819 * 1820 * @param iFildes The file descriptor of the file to truncate. 1821 * @param ullSize The new size of the file. 1822 * 1823 * @return On success, zero is returned. On error, -1 is returned and 1824 #red_errno is set appropriately. 1825 * 1826 * <b>Errno values</b> 1827 * - #RED_EBADF: The @p iFildes argument is not a valid file descriptor open 1828 * for writing. This includes the case where the file descriptor is for a 1829 * directory. 1830 * - #RED_EFBIG: @p ullSize exceeds the maximum file size. 1831 * - #RED_EIO: A disk I/O error occurred. 1832 * - #RED_ENOSPC: Insufficient free space to perform the truncate. 1833 * - #RED_EUSERS: Cannot become a file system user: too many users. 1834 */ red_ftruncate(int32_t iFildes,uint64_t ullSize)1835 int32_t red_ftruncate( int32_t iFildes, 1836 uint64_t ullSize ) 1837 { 1838 REDSTATUS ret; 1839 1840 ret = PosixEnter(); 1841 1842 if( ret == 0 ) 1843 { 1844 REDHANDLE * pHandle; 1845 1846 ret = FildesToHandle( iFildes, FTYPE_FILE, &pHandle ); 1847 1848 if( ret == -RED_EISDIR ) 1849 { 1850 /* Similar to red_write() (see comment there), the RED_EBADF error 1851 * for a non-writable file descriptor takes precedence. 1852 */ 1853 ret = -RED_EBADF; 1854 } 1855 1856 if( ( ret == 0 ) && ( ( pHandle->bFlags & HFLAG_WRITEABLE ) == 0U ) ) 1857 { 1858 ret = -RED_EBADF; 1859 } 1860 1861 #if REDCONF_VOLUME_COUNT > 1U 1862 if( ret == 0 ) 1863 { 1864 ret = RedCoreVolSetCurrent( pHandle->bVolNum ); 1865 } 1866 #endif 1867 1868 if( ret == 0 ) 1869 { 1870 ret = RedCoreFileTruncate( pHandle->ulInode, ullSize ); 1871 } 1872 1873 PosixLeave(); 1874 } 1875 1876 return PosixReturn( ret ); 1877 } 1878 #endif /* if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX_FTRUNCATE == 1 ) */ 1879 1880 1881 /** @brief Get the status of a file or directory. 1882 * 1883 * See the ::REDSTAT type for the details of the information returned. 1884 * 1885 * @param iFildes An open file descriptor for the file whose information is 1886 * to be retrieved. 1887 * @param pStat Pointer to a ::REDSTAT buffer to populate. 1888 * 1889 * @return On success, zero is returned. On error, -1 is returned and 1890 #red_errno is set appropriately. 1891 * 1892 * <b>Errno values</b> 1893 * - #RED_EBADF: The @p iFildes argument is not a valid file descriptor. 1894 * - #RED_EINVAL: @p pStat is `NULL`. 1895 * - #RED_EIO: A disk I/O error occurred. 1896 * - #RED_EUSERS: Cannot become a file system user: too many users. 1897 */ red_fstat(int32_t iFildes,REDSTAT * pStat)1898 int32_t red_fstat( int32_t iFildes, 1899 REDSTAT * pStat ) 1900 { 1901 REDSTATUS ret; 1902 1903 ret = PosixEnter(); 1904 1905 if( ret == 0 ) 1906 { 1907 REDHANDLE * pHandle; 1908 1909 ret = FildesToHandle( iFildes, FTYPE_EITHER, &pHandle ); 1910 1911 #if REDCONF_VOLUME_COUNT > 1U 1912 if( ret == 0 ) 1913 { 1914 ret = RedCoreVolSetCurrent( pHandle->bVolNum ); 1915 } 1916 #endif 1917 1918 if( ret == 0 ) 1919 { 1920 ret = RedCoreStat( pHandle->ulInode, pStat ); 1921 } 1922 1923 PosixLeave(); 1924 } 1925 1926 return PosixReturn( ret ); 1927 } 1928 1929 1930 #if REDCONF_API_POSIX_READDIR == 1 1931 1932 /** @brief Open a directory stream for reading. 1933 * 1934 * @param pszPath The path of the directory to open. 1935 * 1936 * @return On success, returns a pointer to a ::REDDIR object that can be used 1937 * with red_readdir() and red_closedir(). On error, returns `NULL` 1938 * and #red_errno is set appropriately. 1939 * 1940 * <b>Errno values</b> 1941 * - #RED_EINVAL: @p pszPath is `NULL`; or the volume containing the path is 1942 * not mounted. 1943 * - #RED_EIO: A disk I/O error occurred. 1944 * - #RED_ENOENT: A component of @p pszPath does not exist; or the @p pszPath 1945 * argument, after removing the volume prefix, points to an empty string. 1946 * - #RED_ENOTDIR: A component of @p pszPath is a not a directory. 1947 * - #RED_EMFILE: There are no available file descriptors. 1948 * - #RED_EUSERS: Cannot become a file system user: too many users. 1949 */ red_opendir(const char * pszPath)1950 REDDIR * red_opendir( const char * pszPath ) 1951 { 1952 int32_t iFildes; 1953 REDSTATUS ret; 1954 REDDIR * pDir = NULL; 1955 1956 ret = PosixEnter(); 1957 1958 if( ret == 0 ) 1959 { 1960 ret = FildesOpen( pszPath, RED_O_RDONLY, FTYPE_DIR, &iFildes ); 1961 1962 if( ret == 0 ) 1963 { 1964 uint16_t uHandleIdx; 1965 1966 FildesUnpack( iFildes, &uHandleIdx, NULL, NULL ); 1967 pDir = &gaHandle[ uHandleIdx ]; 1968 } 1969 1970 PosixLeave(); 1971 } 1972 1973 REDASSERT( ( pDir == NULL ) == ( ret != 0 ) ); 1974 1975 if( pDir == NULL ) 1976 { 1977 red_errno = -ret; 1978 } 1979 1980 return pDir; 1981 } 1982 1983 1984 /** @brief Read from a directory stream. 1985 * 1986 * The ::REDDIRENT pointer returned by this function will be overwritten by 1987 * subsequent calls on the same @p pDir. Calls with other ::REDDIR objects 1988 * will *not* modify the returned ::REDDIRENT. 1989 * 1990 * If files are added to the directory after it is opened, the new files may 1991 * or may not be returned by this function. If files are deleted, the deleted 1992 * files will not be returned. 1993 * 1994 * This function (like its POSIX equivalent) returns `NULL` in two cases: on 1995 * error and when the end of the directory is reached. To distinguish between 1996 * these two cases, the application should set #red_errno to zero before 1997 * calling this function, and if `NULL` is returned, check if #red_errno is 1998 * still zero. If it is, the end of the directory was reached; otherwise, 1999 * there was an error. 2000 * 2001 * @param pDirStream The directory stream to read from. 2002 * 2003 * @return On success, returns a pointer to a ::REDDIRENT object which is 2004 * populated with directory entry information read from the directory. 2005 * On error, returns `NULL` and #red_errno is set appropriately. If at 2006 * the end of the directory, returns `NULL` but #red_errno is not 2007 * modified. 2008 * 2009 * <b>Errno values</b> 2010 * - #RED_EBADF: @p pDirStream is not an open directory stream. 2011 * - #RED_EIO: A disk I/O error occurred. 2012 * - #RED_EUSERS: Cannot become a file system user: too many users. 2013 */ red_readdir(REDDIR * pDirStream)2014 REDDIRENT * red_readdir( REDDIR * pDirStream ) 2015 { 2016 REDSTATUS ret; 2017 REDDIRENT * pDirEnt = NULL; 2018 2019 ret = PosixEnter(); 2020 2021 if( ret == 0 ) 2022 { 2023 if( !DirStreamIsValid( pDirStream ) ) 2024 { 2025 ret = -RED_EBADF; 2026 } 2027 2028 #if REDCONF_VOLUME_COUNT > 1U 2029 else 2030 { 2031 ret = RedCoreVolSetCurrent( pDirStream->bVolNum ); 2032 } 2033 #endif 2034 2035 if( ret == 0 ) 2036 { 2037 uint32_t ulDirPosition; 2038 2039 /* To save memory, the directory position is stored in the same 2040 * location as the file offset. This would be a bit cleaner using 2041 * a union, but MISRA-C:2012 Rule 19.2 disallows unions. 2042 */ 2043 REDASSERT( pDirStream->ullOffset <= UINT32_MAX ); 2044 ulDirPosition = ( uint32_t ) pDirStream->ullOffset; 2045 2046 ret = RedCoreDirRead( pDirStream->ulInode, &ulDirPosition, pDirStream->dirent.d_name, &pDirStream->dirent.d_ino ); 2047 2048 pDirStream->ullOffset = ulDirPosition; 2049 2050 if( ret == 0 ) 2051 { 2052 /* POSIX extension: return stat information with the dirent. 2053 */ 2054 ret = RedCoreStat( pDirStream->dirent.d_ino, &pDirStream->dirent.d_stat ); 2055 2056 if( ret == 0 ) 2057 { 2058 pDirEnt = &pDirStream->dirent; 2059 } 2060 } 2061 else if( ret == -RED_ENOENT ) 2062 { 2063 /* Reached the end of the directory; return NULL but do not set 2064 * errno. 2065 */ 2066 ret = 0; 2067 } 2068 else 2069 { 2070 /* Miscellaneous error; return NULL and set errno (done below). 2071 */ 2072 } 2073 } 2074 2075 PosixLeave(); 2076 } 2077 2078 if( ret != 0 ) 2079 { 2080 REDASSERT( pDirEnt == NULL ); 2081 2082 red_errno = -ret; 2083 } 2084 2085 return pDirEnt; 2086 } 2087 2088 2089 /** @brief Rewind a directory stream to read it from the beginning. 2090 * 2091 * Similar to closing the directory object and opening it again, but without 2092 * the need for the path. 2093 * 2094 * Since this function (like its POSIX equivalent) cannot return an error, 2095 * it takes no action in error conditions, such as when @p pDirStream is 2096 * invalid. 2097 * 2098 * @param pDirStream The directory stream to rewind. 2099 */ red_rewinddir(REDDIR * pDirStream)2100 void red_rewinddir( REDDIR * pDirStream ) 2101 { 2102 if( PosixEnter() == 0 ) 2103 { 2104 if( DirStreamIsValid( pDirStream ) ) 2105 { 2106 pDirStream->ullOffset = 0U; 2107 } 2108 2109 PosixLeave(); 2110 } 2111 } 2112 2113 2114 /** @brief Close a directory stream. 2115 * 2116 * After calling this function, @p pDirStream should no longer be used. 2117 * 2118 * @param pDirStream The directory stream to close. 2119 * 2120 * @return On success, zero is returned. On error, -1 is returned and 2121 #red_errno is set appropriately. 2122 * 2123 * <b>Errno values</b> 2124 * - #RED_EBADF: @p pDirStream is not an open directory stream. 2125 * - #RED_EUSERS: Cannot become a file system user: too many users. 2126 */ red_closedir(REDDIR * pDirStream)2127 int32_t red_closedir( REDDIR * pDirStream ) 2128 { 2129 REDSTATUS ret; 2130 2131 ret = PosixEnter(); 2132 2133 if( ret == 0 ) 2134 { 2135 if( DirStreamIsValid( pDirStream ) ) 2136 { 2137 /* Mark this handle as unused. 2138 */ 2139 pDirStream->ulInode = INODE_INVALID; 2140 } 2141 else 2142 { 2143 ret = -RED_EBADF; 2144 } 2145 2146 PosixLeave(); 2147 } 2148 2149 return PosixReturn( ret ); 2150 } 2151 #endif /* REDCONF_API_POSIX_READDIR */ 2152 2153 2154 /** @brief Pointer to where the last file system error (errno) is stored. 2155 * 2156 * This function is intended to be used via the #red_errno macro, or a similar 2157 * user-defined macro, that can be used both as an lvalue (writable) and an 2158 * rvalue (readable). 2159 * 2160 * Under normal circumstances, the errno for each task is stored in a 2161 * different location. Applications do not need to worry about one task 2162 * obliterating an error value that another task needed to read. This task 2163 * errno for is initially zero. When one of the POSIX-like APIs returns an 2164 * indication of error, the location for the calling task will be populated 2165 * with the error value. 2166 * 2167 * In some circumstances, this function will return a pointer to a global 2168 * errno location which is shared by multiple tasks. If the calling task is 2169 * not registered as a file system user and all of the task slots are full, 2170 * there can be no task-specific errno, so the global pointer is returned. 2171 * Likewise, if the file system driver is uninitialized, there are no 2172 * registered file system users and this function always returns the pointer 2173 * to the global errno. Under these circumstances, multiple tasks 2174 * manipulating errno could be problematic. 2175 * 2176 * This function never returns `NULL` under any circumstances. The #red_errno 2177 * macro unconditionally dereferences the return value from this function, so 2178 * returning `NULL` could result in a fault. 2179 * 2180 * @return Pointer to where the errno value is stored for this task. 2181 */ red_errnoptr(void)2182 REDSTATUS * red_errnoptr( void ) 2183 { 2184 /* The global errno value, used in single-task configurations and when the 2185 * caller is not (and cannot become) a file system user (which includes 2186 * when the driver is uninitialized). 2187 */ 2188 static REDSTATUS iGlobalErrno = 0; 2189 2190 #if REDCONF_TASK_COUNT == 1U 2191 return &iGlobalErrno; 2192 #else 2193 REDSTATUS * piErrno; 2194 2195 if( gfPosixInited ) 2196 { 2197 uint32_t ulTaskId = RedOsTaskId(); 2198 uint32_t ulIdx; 2199 2200 REDASSERT( ulTaskId != 0U ); 2201 2202 /* If this task has used the file system before, it will already have 2203 * a task slot, which includes the task-specific errno. 2204 */ 2205 RedOsMutexAcquire(); 2206 2207 for( ulIdx = 0U; ulIdx < REDCONF_TASK_COUNT; ulIdx++ ) 2208 { 2209 if( gaTask[ ulIdx ].ulTaskId == ulTaskId ) 2210 { 2211 break; 2212 } 2213 } 2214 2215 RedOsMutexRelease(); 2216 2217 if( ulIdx == REDCONF_TASK_COUNT ) 2218 { 2219 REDSTATUS ret; 2220 2221 /* This task is not a file system user, so try to register it as 2222 * one. This FS mutex must be held in order to register. 2223 */ 2224 RedOsMutexAcquire(); 2225 2226 ret = TaskRegister( &ulIdx ); 2227 2228 RedOsMutexRelease(); 2229 2230 if( ret == 0 ) 2231 { 2232 REDASSERT( gaTask[ ulIdx ].ulTaskId == RedOsTaskId() ); 2233 REDASSERT( gaTask[ ulIdx ].iErrno == 0 ); 2234 2235 piErrno = &gaTask[ ulIdx ].iErrno; 2236 } 2237 else 2238 { 2239 /* Unable to register; use the global errno. 2240 */ 2241 piErrno = &iGlobalErrno; 2242 } 2243 } 2244 else 2245 { 2246 piErrno = &gaTask[ ulIdx ].iErrno; 2247 } 2248 } 2249 else 2250 { 2251 /* There are no registered file system tasks when the driver is 2252 * uninitialized, so use the global errno. 2253 */ 2254 piErrno = &iGlobalErrno; 2255 } 2256 2257 /* This function is not allowed to return NULL. 2258 */ 2259 REDASSERT( piErrno != NULL ); 2260 return piErrno; 2261 #endif /* if REDCONF_TASK_COUNT == 1U */ 2262 } 2263 /** @} */ 2264 2265 /*------------------------------------------------------------------- 2266 * Helper Functions 2267 * -------------------------------------------------------------------*/ 2268 2269 #if ( REDCONF_READ_ONLY == 0 ) && ( ( REDCONF_API_POSIX_UNLINK == 1 ) || ( REDCONF_API_POSIX_RMDIR == 1 ) ) 2270 2271 /** @brief Remove a link to a file or directory. 2272 * 2273 * If the link count becomes zero, the file or directory is deleted. 2274 * 2275 * @param pszPath Path of the link to remove. 2276 * @param type The expected type of the path: file, directory, or either. 2277 * An error is returned if the expected type is file or 2278 * directory and does not match the path. 2279 * 2280 * @return A negated ::REDSTATUS code indicating the operation result. 2281 * 2282 * @retval -RED_EBUSY @p pszPath points to an inode with open handles 2283 * and a link count of one. 2284 * @retval -RED_EINVAL @p pszPath is `NULL`; or the volume containing 2285 * the path is not mounted. 2286 * @retval -RED_EIO A disk I/O error occurred. 2287 * @retval -RED_EISDIR @p type is ::FTYPE_FILE and the path names a 2288 * directory. 2289 * @retval -RED_ENAMETOOLONG @p pszName is too long. 2290 * @retval -RED_ENOENT The path does not name an existing file; or 2291 * @p pszPath, after removing the volume prefix, 2292 * points to an empty string. 2293 * @retval -RED_ENOTDIR @p type is ::FTYPE_DIR and the path does not 2294 * name a directory. 2295 * @retval -RED_ENOTEMPTY @p pszPath is a directory which is not empty. 2296 * @retval -RED_ENOSPC The file system does not have enough space to 2297 * modify the parent directory to perform the 2298 * deletion. 2299 */ UnlinkSub(const char * pszPath,FTYPE type)2300 static REDSTATUS UnlinkSub( const char * pszPath, 2301 FTYPE type ) 2302 { 2303 uint8_t bVolNum; 2304 const char * pszLocalPath; 2305 REDSTATUS ret; 2306 2307 ret = RedPathSplit( pszPath, &bVolNum, &pszLocalPath ); 2308 2309 #if REDCONF_VOLUME_COUNT > 1U 2310 if( ret == 0 ) 2311 { 2312 ret = RedCoreVolSetCurrent( bVolNum ); 2313 } 2314 #endif 2315 2316 if( ret == 0 ) 2317 { 2318 const char * pszName; 2319 uint32_t ulPInode; 2320 2321 ret = RedPathToName( pszLocalPath, &ulPInode, &pszName ); 2322 2323 if( ret == 0 ) 2324 { 2325 uint32_t ulInode; 2326 2327 ret = RedCoreLookup( ulPInode, pszName, &ulInode ); 2328 2329 /* ModeTypeCheck() always passes when the type is FTYPE_EITHER, so 2330 * skip stat'ing the inode in that case. 2331 */ 2332 if( ( ret == 0 ) && ( type != FTYPE_EITHER ) ) 2333 { 2334 REDSTAT InodeStat; 2335 2336 ret = RedCoreStat( ulInode, &InodeStat ); 2337 2338 if( ret == 0 ) 2339 { 2340 ret = ModeTypeCheck( InodeStat.st_mode, type ); 2341 } 2342 } 2343 2344 if( ret == 0 ) 2345 { 2346 ret = InodeUnlinkCheck( ulInode ); 2347 } 2348 2349 if( ret == 0 ) 2350 { 2351 ret = RedCoreUnlink( ulPInode, pszName ); 2352 } 2353 } 2354 } 2355 2356 return ret; 2357 } 2358 #endif /* (REDCONF_API_POSIX_UNLINK == 1) || (REDCONF_API_POSIX_RMDIR == 1) */ 2359 2360 2361 /** @brief Get a file descriptor for a path. 2362 * 2363 * @param pszPath Path to a file to open. 2364 * @param ulOpenMode The RED_O_* flags the descriptor is opened with. 2365 * @param type Indicates the expected descriptor type: file, directory, 2366 * or either. 2367 * @param piFildes On successful return, populated with the file 2368 * descriptor. 2369 * 2370 * @return A negated ::REDSTATUS code indicating the operation result. 2371 * 2372 * @retval 0 Operation was successful. 2373 * @retval -RED_EINVAL @p piFildes is `NULL`; or @p pszPath is `NULL`; 2374 * or the volume is not mounted. 2375 * @retval -RED_EMFILE There are no available handles. 2376 * @retval -RED_EEXIST Using #RED_O_CREAT and #RED_O_EXCL, and the 2377 * indicated path already exists. 2378 * @retval -RED_EISDIR The path names a directory and @p ulOpenMode 2379 * includes #RED_O_WRONLY or #RED_O_RDWR. 2380 * @retval -RED_ENOENT #RED_O_CREAT is not set and the named file does 2381 * not exist; or #RED_O_CREAT is set and the parent 2382 * directory does not exist; or the volume does not 2383 * exist; or the @p pszPath argument, after 2384 * removing the volume prefix, points to an empty 2385 * string. 2386 * @retval -RED_EIO A disk I/O error occurred. 2387 * @retval -RED_ENAMETOOLONG The length of a component of @p pszPath is 2388 * longer than #REDCONF_NAME_MAX. 2389 * @retval -RED_ENFILE Attempting to create a file but the file system 2390 * has used all available inode slots. 2391 * @retval -RED_ENOSPC The file does not exist and #RED_O_CREAT was 2392 * specified, but there is insufficient free space 2393 * to expand the directory or to create the new 2394 * file. 2395 * @retval -RED_ENOTDIR A component of the prefix in @p pszPath does not 2396 * name a directory. 2397 * @retval -RED_EROFS The path resides on a read-only file system and 2398 * a write operation was requested. 2399 */ FildesOpen(const char * pszPath,uint32_t ulOpenMode,FTYPE type,int32_t * piFildes)2400 static REDSTATUS FildesOpen( const char * pszPath, 2401 uint32_t ulOpenMode, 2402 FTYPE type, 2403 int32_t * piFildes ) 2404 { 2405 uint8_t bVolNum; 2406 const char * pszLocalPath; 2407 REDSTATUS ret; 2408 2409 ret = RedPathSplit( pszPath, &bVolNum, &pszLocalPath ); 2410 2411 if( ret == 0 ) 2412 { 2413 if( piFildes == NULL ) 2414 { 2415 ret = -RED_EINVAL; 2416 } 2417 2418 #if REDCONF_READ_ONLY == 0 2419 else if( gaRedVolume[ bVolNum ].fReadOnly && ( ulOpenMode != RED_O_RDONLY ) ) 2420 { 2421 ret = -RED_EROFS; 2422 } 2423 #endif 2424 else 2425 { 2426 uint16_t uHandleIdx; 2427 REDHANDLE * pHandle = NULL; 2428 2429 /* Search for an unused handle. 2430 */ 2431 for( uHandleIdx = 0U; uHandleIdx < REDCONF_HANDLE_COUNT; uHandleIdx++ ) 2432 { 2433 if( gaHandle[ uHandleIdx ].ulInode == INODE_INVALID ) 2434 { 2435 pHandle = &gaHandle[ uHandleIdx ]; 2436 break; 2437 } 2438 } 2439 2440 /* Error if all the handles are in use. 2441 */ 2442 if( pHandle == NULL ) 2443 { 2444 ret = -RED_EMFILE; 2445 } 2446 else 2447 { 2448 bool fCreated = false; 2449 uint16_t uMode = 0U; 2450 uint32_t ulInode = 0U; /* Init'd to quiet warnings. */ 2451 2452 #if REDCONF_VOLUME_COUNT > 1U 2453 ret = RedCoreVolSetCurrent( bVolNum ); 2454 2455 if( ret == 0 ) 2456 #endif 2457 { 2458 #if REDCONF_READ_ONLY == 0 2459 if( ( ulOpenMode & RED_O_CREAT ) != 0U ) 2460 { 2461 uint32_t ulPInode; 2462 const char * pszName; 2463 2464 ret = RedPathToName( pszLocalPath, &ulPInode, &pszName ); 2465 2466 if( ret == 0 ) 2467 { 2468 ret = RedCoreCreate( ulPInode, pszName, false, &ulInode ); 2469 2470 if( ret == 0 ) 2471 { 2472 fCreated = true; 2473 } 2474 else if( ( ret == -RED_EEXIST ) && ( ( ulOpenMode & RED_O_EXCL ) == 0U ) ) 2475 { 2476 /* If the path already exists and that's OK, 2477 * lookup its inode number. 2478 */ 2479 ret = RedCoreLookup( ulPInode, pszName, &ulInode ); 2480 } 2481 else 2482 { 2483 /* No action, just propagate the error. 2484 */ 2485 } 2486 } 2487 } 2488 else 2489 #endif /* if REDCONF_READ_ONLY == 0 */ 2490 { 2491 ret = RedPathLookup( pszLocalPath, &ulInode ); 2492 } 2493 } 2494 2495 /* If we created the inode, none of the below stuff is 2496 * necessary. This is important from an error handling 2497 * perspective -- we do not need code to delete the created 2498 * inode on error. 2499 */ 2500 if( !fCreated ) 2501 { 2502 if( ret == 0 ) 2503 { 2504 REDSTAT s; 2505 2506 ret = RedCoreStat( ulInode, &s ); 2507 2508 if( ret == 0 ) 2509 { 2510 uMode = s.st_mode; 2511 } 2512 } 2513 2514 /* Error if the inode is not of the expected type. 2515 */ 2516 if( ret == 0 ) 2517 { 2518 ret = ModeTypeCheck( uMode, type ); 2519 } 2520 2521 /* Directories must always be opened with O_RDONLY. 2522 */ 2523 if( ( ret == 0 ) && RED_S_ISDIR( uMode ) && ( ( ulOpenMode & RED_O_RDONLY ) == 0U ) ) 2524 { 2525 ret = -RED_EISDIR; 2526 } 2527 2528 #if ( REDCONF_READ_ONLY == 0 ) && ( REDCONF_API_POSIX_FTRUNCATE == 1 ) 2529 if( ( ret == 0 ) && ( ( ulOpenMode & RED_O_TRUNC ) != 0U ) ) 2530 { 2531 ret = RedCoreFileTruncate( ulInode, UINT64_SUFFIX( 0 ) ); 2532 } 2533 #endif 2534 } 2535 2536 if( ret == 0 ) 2537 { 2538 int32_t iFildes; 2539 2540 RedMemSet( pHandle, 0U, sizeof( *pHandle ) ); 2541 2542 /* Populate this handle, marking it as in use. 2543 */ 2544 pHandle->ulInode = ulInode; 2545 pHandle->bVolNum = bVolNum; 2546 2547 if( RED_S_ISDIR( uMode ) ) 2548 { 2549 pHandle->bFlags |= HFLAG_DIRECTORY; 2550 } 2551 2552 if( ( ( ulOpenMode & RED_O_RDONLY ) != 0U ) || ( ( ulOpenMode & RED_O_RDWR ) != 0U ) ) 2553 { 2554 pHandle->bFlags |= HFLAG_READABLE; 2555 } 2556 2557 #if REDCONF_READ_ONLY == 0 2558 if( ( ( ulOpenMode & RED_O_WRONLY ) != 0U ) || ( ( ulOpenMode & RED_O_RDWR ) != 0U ) ) 2559 { 2560 pHandle->bFlags |= HFLAG_WRITEABLE; 2561 } 2562 2563 if( ( ulOpenMode & RED_O_APPEND ) != 0U ) 2564 { 2565 pHandle->bFlags |= HFLAG_APPENDING; 2566 } 2567 #endif 2568 2569 iFildes = FildesPack( uHandleIdx, bVolNum ); 2570 2571 if( iFildes == -1 ) 2572 { 2573 /* It should be impossible to get here, unless there 2574 * is memory corruption. 2575 */ 2576 REDERROR(); 2577 ret = -RED_EFUBAR; 2578 } 2579 else 2580 { 2581 *piFildes = iFildes; 2582 } 2583 } 2584 } 2585 } 2586 } 2587 2588 return ret; 2589 } 2590 2591 2592 /** @brief Close a file descriptor. 2593 * 2594 * @param iFildes The file descriptor to close. 2595 * 2596 * @return A negated ::REDSTATUS code indicating the operation result. 2597 * 2598 * @retval 0 Operation was successful. 2599 * @retval -RED_EBADF @p iFildes is not a valid file descriptor. 2600 * @retval -RED_EIO A disk I/O error occurred. 2601 */ FildesClose(int32_t iFildes)2602 static REDSTATUS FildesClose( int32_t iFildes ) 2603 { 2604 REDHANDLE * pHandle; 2605 REDSTATUS ret; 2606 2607 ret = FildesToHandle( iFildes, FTYPE_EITHER, &pHandle ); 2608 2609 #if REDCONF_READ_ONLY == 0 2610 #if REDCONF_VOLUME_COUNT > 1U 2611 if( ret == 0 ) 2612 { 2613 ret = RedCoreVolSetCurrent( pHandle->bVolNum ); 2614 } 2615 #endif 2616 2617 /* No core event for close, so this transaction flag needs to be 2618 * implemented here. 2619 */ 2620 if( ret == 0 ) 2621 { 2622 uint32_t ulTransMask; 2623 2624 ret = RedCoreTransMaskGet( &ulTransMask ); 2625 2626 if( ( ret == 0 ) && ( ( ulTransMask & RED_TRANSACT_CLOSE ) != 0U ) ) 2627 { 2628 ret = RedCoreVolTransact(); 2629 } 2630 } 2631 #endif /* if REDCONF_READ_ONLY == 0 */ 2632 2633 if( ret == 0 ) 2634 { 2635 /* Mark this handle as unused. 2636 */ 2637 pHandle->ulInode = INODE_INVALID; 2638 } 2639 2640 return ret; 2641 } 2642 2643 2644 /** @brief Convert a file descriptor into a handle pointer. 2645 * 2646 * Also validates the file descriptor. 2647 * 2648 * @param iFildes The file descriptor for which to get a handle. 2649 * @param expectedType The expected type of the file descriptor: ::FTYPE_DIR, 2650 * ::FTYPE_FILE, or ::FTYPE_EITHER. 2651 * @param ppHandle On successful return, populated with a pointer to the 2652 * handle associated with @p iFildes. 2653 * 2654 * @return A negated ::REDSTATUS code indicating the operation result. 2655 * 2656 * @retval 0 Operation was successful. 2657 * @retval -RED_EBADF @p iFildes is not a valid file descriptor. 2658 * @retval -RED_EINVAL @p ppHandle is `NULL`. 2659 * @retval -RED_EISDIR Expected a file, but the file descriptor is for a 2660 * directory. 2661 * @retval -RED_ENOTDIR Expected a directory, but the file descriptor is for 2662 * a file. 2663 */ FildesToHandle(int32_t iFildes,FTYPE expectedType,REDHANDLE ** ppHandle)2664 static REDSTATUS FildesToHandle( int32_t iFildes, 2665 FTYPE expectedType, 2666 REDHANDLE ** ppHandle ) 2667 { 2668 REDSTATUS ret; 2669 2670 if( ppHandle == NULL ) 2671 { 2672 REDERROR(); 2673 ret = -RED_EINVAL; 2674 } 2675 else if( iFildes < FD_MIN ) 2676 { 2677 ret = -RED_EBADF; 2678 } 2679 else 2680 { 2681 uint16_t uHandleIdx; 2682 uint8_t bVolNum; 2683 uint16_t uGeneration; 2684 2685 FildesUnpack( iFildes, &uHandleIdx, &bVolNum, &uGeneration ); 2686 2687 if( ( uHandleIdx >= REDCONF_HANDLE_COUNT ) || 2688 ( bVolNum >= REDCONF_VOLUME_COUNT ) || 2689 ( gaHandle[ uHandleIdx ].ulInode == INODE_INVALID ) || 2690 ( gaHandle[ uHandleIdx ].bVolNum != bVolNum ) || 2691 ( gauGeneration[ bVolNum ] != uGeneration ) ) 2692 { 2693 ret = -RED_EBADF; 2694 } 2695 else if( ( expectedType == FTYPE_FILE ) && ( ( gaHandle[ uHandleIdx ].bFlags & HFLAG_DIRECTORY ) != 0U ) ) 2696 { 2697 ret = -RED_EISDIR; 2698 } 2699 else if( ( expectedType == FTYPE_DIR ) && ( ( gaHandle[ uHandleIdx ].bFlags & HFLAG_DIRECTORY ) == 0U ) ) 2700 { 2701 ret = -RED_ENOTDIR; 2702 } 2703 else 2704 { 2705 *ppHandle = &gaHandle[ uHandleIdx ]; 2706 ret = 0; 2707 } 2708 } 2709 2710 return ret; 2711 } 2712 2713 2714 /** @brief Pack a file descriptor. 2715 * 2716 * @param uHandleIdx The index of the file handle that will be associated 2717 * with this file descriptor. 2718 * @param bVolNum The volume which contains the file or directory this 2719 * file descriptor was opened against. 2720 * 2721 * @return The packed file descriptor. 2722 */ FildesPack(uint16_t uHandleIdx,uint8_t bVolNum)2723 static int32_t FildesPack( uint16_t uHandleIdx, 2724 uint8_t bVolNum ) 2725 { 2726 int32_t iFildes; 2727 2728 if( ( uHandleIdx >= REDCONF_HANDLE_COUNT ) || ( bVolNum >= REDCONF_VOLUME_COUNT ) ) 2729 { 2730 REDERROR(); 2731 iFildes = -1; 2732 } 2733 else 2734 { 2735 uint32_t ulFdBits; 2736 2737 REDASSERT( gauGeneration[ bVolNum ] <= FD_GEN_MAX ); 2738 REDASSERT( gauGeneration[ bVolNum ] != 0U ); 2739 2740 ulFdBits = gauGeneration[ bVolNum ]; 2741 ulFdBits <<= FD_VOL_BITS; 2742 ulFdBits |= bVolNum; 2743 ulFdBits <<= FD_IDX_BITS; 2744 ulFdBits |= uHandleIdx; 2745 2746 iFildes = ( int32_t ) ulFdBits; 2747 2748 if( iFildes < FD_MIN ) 2749 { 2750 REDERROR(); 2751 iFildes = -1; 2752 } 2753 } 2754 2755 return iFildes; 2756 } 2757 2758 2759 /** @brief Unpack a file descriptor. 2760 * 2761 * @param iFildes The file descriptor to unpack. 2762 * @param puHandleIdx If non-NULL, populated with the handle index extracted 2763 * from the file descriptor. 2764 * @param pbVolNum If non-NULL, populated with the volume number extracted 2765 * from the file descriptor. 2766 * @param puGeneration If non-NULL, populated with the generation number 2767 * extracted from the file descriptor. 2768 */ FildesUnpack(int32_t iFildes,uint16_t * puHandleIdx,uint8_t * pbVolNum,uint16_t * puGeneration)2769 static void FildesUnpack( int32_t iFildes, 2770 uint16_t * puHandleIdx, 2771 uint8_t * pbVolNum, 2772 uint16_t * puGeneration ) 2773 { 2774 uint32_t ulFdBits = ( uint32_t ) iFildes; 2775 2776 REDASSERT( iFildes >= FD_MIN ); 2777 2778 if( puHandleIdx != NULL ) 2779 { 2780 *puHandleIdx = ( uint16_t ) ( ulFdBits & FD_IDX_MAX ); 2781 } 2782 2783 ulFdBits >>= FD_IDX_BITS; 2784 2785 if( pbVolNum != NULL ) 2786 { 2787 *pbVolNum = ( uint8_t ) ( ulFdBits & FD_VOL_MAX ); 2788 } 2789 2790 ulFdBits >>= FD_VOL_BITS; 2791 2792 if( puGeneration != NULL ) 2793 { 2794 *puGeneration = ( uint16_t ) ( ulFdBits & FD_GEN_MAX ); 2795 } 2796 } 2797 2798 2799 #if REDCONF_API_POSIX_READDIR == 1 2800 2801 /** @brief Validate a directory stream object. 2802 * 2803 * @param pDirStream The directory stream to validate. 2804 * 2805 * @return Whether the directory stream is valid. 2806 * 2807 * @retval true The directory stream object appears valid. 2808 * @retval false The directory stream object is invalid. 2809 */ DirStreamIsValid(const REDDIR * pDirStream)2810 static bool DirStreamIsValid( const REDDIR * pDirStream ) 2811 { 2812 bool fRet = true; 2813 2814 if( pDirStream == NULL ) 2815 { 2816 fRet = false; 2817 } 2818 else 2819 { 2820 uint16_t uHandleIdx; 2821 2822 /* pDirStream should be a pointer to one of the handles. 2823 * 2824 * A good compiler will optimize this loop into a bounds check and an 2825 * alignment check. 2826 */ 2827 for( uHandleIdx = 0U; uHandleIdx < REDCONF_HANDLE_COUNT; uHandleIdx++ ) 2828 { 2829 if( pDirStream == &gaHandle[ uHandleIdx ] ) 2830 { 2831 break; 2832 } 2833 } 2834 2835 if( uHandleIdx < REDCONF_HANDLE_COUNT ) 2836 { 2837 /* The handle must be in use, have a valid volume number, and be a 2838 * directory handle. 2839 */ 2840 if( ( pDirStream->ulInode == INODE_INVALID ) || 2841 ( pDirStream->bVolNum >= REDCONF_VOLUME_COUNT ) || 2842 ( ( pDirStream->bFlags & HFLAG_DIRECTORY ) == 0U ) ) 2843 { 2844 fRet = false; 2845 } 2846 } 2847 else 2848 { 2849 /* pDirStream is a non-null pointer, but it is not a pointer to one 2850 * of our handles. 2851 */ 2852 fRet = false; 2853 } 2854 } 2855 2856 return fRet; 2857 } 2858 #endif /* if REDCONF_API_POSIX_READDIR == 1 */ 2859 2860 2861 /** @brief Enter the file system driver. 2862 * 2863 * @return A negated ::REDSTATUS code indicating the operation result. 2864 * 2865 * @retval 0 Operation was successful. 2866 * @retval -RED_EINVAL The file system driver is uninitialized. 2867 * @retval -RED_EUSERS Cannot become a file system user: too many users. 2868 */ PosixEnter(void)2869 static REDSTATUS PosixEnter( void ) 2870 { 2871 REDSTATUS ret; 2872 2873 if( gfPosixInited ) 2874 { 2875 #if REDCONF_TASK_COUNT > 1U 2876 RedOsMutexAcquire(); 2877 2878 ret = TaskRegister( NULL ); 2879 2880 if( ret != 0 ) 2881 { 2882 RedOsMutexRelease(); 2883 } 2884 #else 2885 ret = 0; 2886 #endif 2887 } 2888 else 2889 { 2890 ret = -RED_EINVAL; 2891 } 2892 2893 return ret; 2894 } 2895 2896 2897 /** @brief Leave the file system driver. 2898 */ PosixLeave(void)2899 static void PosixLeave( void ) 2900 { 2901 /* If the driver was uninitialized, PosixEnter() should have failed and we 2902 * should not be calling PosixLeave(). 2903 */ 2904 REDASSERT( gfPosixInited ); 2905 2906 #if REDCONF_TASK_COUNT > 1U 2907 RedOsMutexRelease(); 2908 #endif 2909 } 2910 2911 2912 /** @brief Check that a mode is consistent with the given expected type. 2913 * 2914 * @param uMode An inode mode, indicating whether the inode is a file 2915 * or a directory. 2916 * @param expectedType The expected type: ::FTYPE_FILE, ::FTYPE_DIR, or 2917 * ::FTYPE_EITHER. 2918 * 2919 * @return A negated ::REDSTATUS code indicating the operation result. 2920 * 2921 * @retval 0 Operation was successful. 2922 * @retval -RED_EISDIR Expected type is file, actual type is directory. 2923 * @retval -RED_ENOTDIR Expected type is directory, actual type is file. 2924 */ ModeTypeCheck(uint16_t uMode,FTYPE expectedType)2925 static REDSTATUS ModeTypeCheck( uint16_t uMode, 2926 FTYPE expectedType ) 2927 { 2928 REDSTATUS ret; 2929 2930 if( ( expectedType == FTYPE_FILE ) && RED_S_ISDIR( uMode ) ) 2931 { 2932 /* Expected file, found directory. 2933 */ 2934 ret = -RED_EISDIR; 2935 } 2936 else if( ( expectedType == FTYPE_DIR ) && RED_S_ISREG( uMode ) ) 2937 { 2938 /* Expected directory, found file. 2939 */ 2940 ret = -RED_ENOTDIR; 2941 } 2942 else 2943 { 2944 /* No expected type or found what we expected. 2945 */ 2946 ret = 0; 2947 } 2948 2949 return ret; 2950 } 2951 2952 2953 #if ( REDCONF_READ_ONLY == 0 ) && ( ( REDCONF_API_POSIX_UNLINK == 1 ) || ( REDCONF_API_POSIX_RMDIR == 1 ) || ( ( REDCONF_API_POSIX_RENAME == 1 ) && ( REDCONF_RENAME_ATOMIC == 1 ) ) ) 2954 2955 /** @brief Check whether an inode can be unlinked. 2956 * 2957 * If an inode has a link count of 1 (meaning unlinking another name would 2958 * result in the deletion of the inode) and open handles, it cannot be deleted 2959 * since this would break open handles. 2960 * 2961 * @param ulInode The inode whose name is to be unlinked. 2962 * 2963 * @return A negated ::REDSTATUS code indicating the operation result. 2964 * 2965 * @retval 0 Operation was successful. 2966 * @retval -RED_EBADF @p ulInode is not a valid inode. 2967 * @retval -RED_EBUSY The inode has a link count of one and open handles. 2968 * @retval -RED_EIO A disk I/O error occurred. 2969 */ InodeUnlinkCheck(uint32_t ulInode)2970 static REDSTATUS InodeUnlinkCheck( uint32_t ulInode ) 2971 { 2972 uint16_t uHandleIdx; 2973 REDSTATUS ret; 2974 2975 #if REDCONF_API_POSIX_LINK == 0 2976 ret = 0; 2977 #else 2978 REDSTAT InodeStat; 2979 2980 ret = RedCoreStat( ulInode, &InodeStat ); 2981 2982 /* We only need to check for open handles if the inode is down to its last 2983 * link. If it has multiple links, the inode will continue to exist, so 2984 * deleting the name will not break the open handles. 2985 */ 2986 if( ( ret == 0 ) && ( InodeStat.st_nlink == 1U ) ) 2987 #endif 2988 { 2989 for( uHandleIdx = 0U; uHandleIdx < REDCONF_HANDLE_COUNT; uHandleIdx++ ) 2990 { 2991 if( ( gaHandle[ uHandleIdx ].ulInode == ulInode ) && ( gaHandle[ uHandleIdx ].bVolNum == gbRedVolNum ) ) 2992 { 2993 ret = -RED_EBUSY; 2994 break; 2995 } 2996 } 2997 } 2998 2999 return ret; 3000 } 3001 #endif /* if ( REDCONF_READ_ONLY == 0 ) && ( ( REDCONF_API_POSIX_UNLINK == 1 ) || ( REDCONF_API_POSIX_RMDIR == 1 ) || ( ( REDCONF_API_POSIX_RENAME == 1 ) && ( REDCONF_RENAME_ATOMIC == 1 ) ) ) */ 3002 3003 3004 #if REDCONF_TASK_COUNT > 1U 3005 3006 /** @brief Register a task as a file system user, if it is not already 3007 * registered as one. 3008 * 3009 * The caller must hold the FS mutex. 3010 * 3011 * @param pulTaskIdx On successful return, if non-NULL, populated with the 3012 * index of the task slot assigned to the calling task. 3013 * This is populated whether or not the task had already 3014 * been registered. 3015 * 3016 * @return A negated ::REDSTATUS code indicating the operation result. 3017 * 3018 * @retval 0 Operation was successful. 3019 * @retval -RED_EUSERS Cannot become a file system user: too many users. 3020 */ TaskRegister(uint32_t * pulTaskIdx)3021 static REDSTATUS TaskRegister( uint32_t * pulTaskIdx ) 3022 { 3023 uint32_t ulTaskId = RedOsTaskId(); 3024 uint32_t ulFirstFreeIdx = REDCONF_TASK_COUNT; 3025 uint32_t ulIdx; 3026 REDSTATUS ret; 3027 3028 REDASSERT( ulTaskId != 0U ); 3029 3030 /* Scan the task slots to determine if the task is registered as a file 3031 * system task. 3032 */ 3033 for( ulIdx = 0U; ulIdx < REDCONF_TASK_COUNT; ulIdx++ ) 3034 { 3035 if( gaTask[ ulIdx ].ulTaskId == ulTaskId ) 3036 { 3037 break; 3038 } 3039 3040 if( ( ulFirstFreeIdx == REDCONF_TASK_COUNT ) && ( gaTask[ ulIdx ].ulTaskId == 0U ) ) 3041 { 3042 ulFirstFreeIdx = ulIdx; 3043 } 3044 } 3045 3046 if( ulIdx == REDCONF_TASK_COUNT ) 3047 { 3048 /* Task not already registered. 3049 */ 3050 if( ulFirstFreeIdx == REDCONF_TASK_COUNT ) 3051 { 3052 /* Cannot register task, no more slots. 3053 */ 3054 ret = -RED_EUSERS; 3055 } 3056 else 3057 { 3058 /* Registering task. 3059 */ 3060 ulIdx = ulFirstFreeIdx; 3061 gaTask[ ulIdx ].ulTaskId = ulTaskId; 3062 ret = 0; 3063 } 3064 } 3065 else 3066 { 3067 /* Task already registered. 3068 */ 3069 ret = 0; 3070 } 3071 3072 if( ( ret == 0 ) && ( pulTaskIdx != NULL ) ) 3073 { 3074 *pulTaskIdx = ulIdx; 3075 } 3076 3077 return ret; 3078 } 3079 #endif /* REDCONF_TASK_COUNT > 1U */ 3080 3081 3082 /** @brief Convert an error value into a simple 0 or -1 return. 3083 * 3084 * This function is simple, but what it does is needed in many places. It 3085 * returns zero if @p iError is zero (meaning success) or it returns -1 if 3086 * @p iError is nonzero (meaning error). Also, if @p iError is nonzero, it 3087 * is saved in red_errno. 3088 * 3089 * @param iError The error value. 3090 * 3091 * @return Returns 0 if @p iError is 0; otherwise, returns -1. 3092 */ PosixReturn(REDSTATUS iError)3093 static int32_t PosixReturn( REDSTATUS iError ) 3094 { 3095 int32_t iReturn; 3096 3097 if( iError == 0 ) 3098 { 3099 iReturn = 0; 3100 } 3101 else 3102 { 3103 iReturn = -1; 3104 3105 /* The errors should be negative, and errno positive. 3106 */ 3107 REDASSERT( iError < 0 ); 3108 red_errno = -iError; 3109 } 3110 3111 return iReturn; 3112 } 3113 3114 3115 #endif /* REDCONF_API_POSIX == 1 */ 3116