1 //==========================================================================
5 // Fileio file operations
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
13 // eCos is free software; you can redistribute it and/or modify it under
14 // the terms of the GNU General Public License as published by the Free
15 // Software Foundation; either version 2 or (at your option) any later version.
17 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
18 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 // You should have received a copy of the GNU General Public License along
23 // with eCos; if not, write to the Free Software Foundation, Inc.,
24 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 // As a special exception, if other files instantiate templates or use macros
27 // or inline functions from this file, or you compile this file and link it
28 // with other works to produce a work based on this file, this file does not
29 // by itself cause the resulting work to be covered by the GNU General Public
30 // License. However the source code for this file must still be made available
31 // in accordance with section (3) of the GNU General Public License.
33 // This exception does not invalidate any other reasons why a work based on
34 // this file might be covered by the GNU General Public License.
36 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
37 // at http://sources.redhat.com/ecos/ecos-license/
38 // -------------------------------------------
39 //####ECOSGPLCOPYRIGHTEND####
40 //==========================================================================
41 //#####DESCRIPTIONBEGIN####
44 // Contributors: nickg
46 // Purpose: Fileio file operations
47 // Description: These are the functions that operate on files as a whole,
48 // such as open(), creat(), mkdir() etc.
52 //####DESCRIPTIONEND####
54 //==========================================================================
56 #include <pkgconf/hal.h>
57 #include <pkgconf/io_fileio.h>
58 #include <pkgconf/isoinfra.h>
60 #include <cyg/infra/cyg_trac.h> // tracing macros
61 #include <cyg/infra/cyg_ass.h> // assertion macros
63 #include <string.h> // string functions
65 #include <stdio.h> // stdin, stdout, stderr
67 #include "fio.h" // Private header
69 //==========================================================================
70 // Implement filesystem locking protocol.
72 #define LOCK_FS( _mte_ ) { \
73 CYG_ASSERT(_mte_ != NULL, "Bad mount table entry"); \
74 CYG_ASSERT(_mte_->fs != NULL, "Bad mount filesystem entry"); \
75 cyg_fs_lock( _mte_, (_mte_)->fs->syncmode); \
78 #define UNLOCK_FS( _mte_ ) cyg_fs_unlock( _mte_, (_mte_)->fs->syncmode)
80 //==========================================================================
81 // A local strcpy clone that returns a pointer to the end of the copied
82 // string, not the beginning.
84 static char *my_strcpy( char *s1, const char *s2 )
86 while( (*s1++ = *s2++) != 0);
90 //==========================================================================
91 // Compare a pathname fragment with an element in a pathname. This
92 // deals with zero or separator termination and avoids substring
95 static int pathcmp( const char *path, const char *name )
97 while( *path == *name && *path != '\0' )
99 if( *name != '\0' ) return false;
100 if( *path == '/' || *path == '\0' ) return true;
104 //==========================================================================
107 #ifdef CYGPKG_IO_FILEIO_TRACK_CWD
109 // buffer for storing CWD path
110 static char cwd[PATH_MAX];
111 static size_t cwd_size = 0;
114 static void update_cwd( cyg_mtab_entry *mte, cyg_dir dir, const char *path )
118 if( mte != cyg_cdir_mtab_entry || dir != cyg_cdir_dir )
120 // Here, the path is relative to the root of the filesystem,
121 // or in a totally new filesystem, initialize the cwd with the
122 // mount point name of the filesystem.
124 p = my_strcpy( p, mte->name );
126 else p = cwd+cwd_size;
128 // We must now copy the path into the cwd buffer while dealing
129 // with any "." and ".." components textually.
131 while( *path != '\0' )
133 // skip any stray directory separators.
134 if( *path == '/' ) path++;
136 // Look for a "." entry and just ignore it.
137 if( pathcmp( path, "." ) )
143 // Look for a ".." entry. If found, chew off the last cwd
145 if( pathcmp( path, ".." ) )
147 while( *(--p) != '/' ); // back up to last '/'
148 path += 2; // skip "..".
152 // Otherwise just copy the name fragment over to the cwd.
155 *p++ = '/'; // add a directory separator
156 while( *path != '/' && *path != '\0' )
161 // Zero terminate the cwd.
171 static Cyg_Mutex getcwd_lock CYGBLD_ATTRIB_INIT_PRI(CYG_INIT_IO_FS);
177 //==========================================================================
180 __externC int open( const char *path, int oflag, ... )
184 // we want to be sure we pull in stdin/out/err, so they can be
185 // assigned to fds 0, 1 and 2
186 #ifdef CYGINT_ISO_STDIO_STREAMS
187 CYG_REFERENCE_OBJECT(stdin);
188 CYG_REFERENCE_OBJECT(stdout);
189 CYG_REFERENCE_OBJECT(stderr);
192 CYG_CANCELLATION_POINT;
197 cyg_mtab_entry *mte = cyg_cdir_mtab_entry;
198 cyg_dir dir = cyg_cdir_dir;
199 const char *name = path;
201 // At least one of O_RDONLY, O_WRONLY, O_RDWR must be provided
202 if( (oflag & O_RDWR) == 0 )
203 FILEIO_RETURN(EINVAL);
205 fd = cyg_fd_alloc(0);
208 FILEIO_RETURN(EMFILE);
210 file = cyg_file_alloc();
215 FILEIO_RETURN(ENFILE);
218 ret = cyg_mtab_lookup( &dir, &name, &mte );
224 FILEIO_RETURN(ENOENT);
229 ret = mte->fs->open( mte, dir, name, oflag, file );
241 file->f_syncmode = mte->fs->syncmode;
243 cyg_fd_assign( fd, file );
245 FILEIO_RETURN_VALUE(fd);
248 //==========================================================================
251 __externC int creat( const char *path, mode_t mode )
253 return open( path, O_WRONLY | O_CREAT | O_TRUNC, mode );
257 //==========================================================================
258 // Unlink/remove a file
260 __externC int unlink( const char *path )
265 cyg_mtab_entry *mte = cyg_cdir_mtab_entry;
266 cyg_dir dir = cyg_cdir_dir;
267 const char *name = path;
269 ret = cyg_mtab_lookup( &dir, &name, &mte );
272 FILEIO_RETURN(ENOENT);
276 ret = mte->fs->unlink( mte, dir, name );
283 //==========================================================================
286 __externC int mkdir( const char *path, mode_t mode )
291 cyg_mtab_entry *mte = cyg_cdir_mtab_entry;
292 cyg_dir dir = cyg_cdir_dir;
293 const char *name = path;
297 ret = cyg_mtab_lookup( &dir, &name, &mte );
300 FILEIO_RETURN(ENOENT);
304 ret = mte->fs->mkdir( mte, dir, name );
311 //==========================================================================
312 // Remove a directory
314 __externC int rmdir( const char *path )
319 cyg_mtab_entry *mte = cyg_cdir_mtab_entry;
320 cyg_dir dir = cyg_cdir_dir;
321 const char *name = path;
323 ret = cyg_mtab_lookup( &dir, &name, &mte );
326 FILEIO_RETURN(ENOENT);
330 ret = mte->fs->rmdir( mte, dir, name );
337 //==========================================================================
340 __externC int rename( const char *path1, const char *path2 ) __THROW
345 cyg_mtab_entry *mte1 = cyg_cdir_mtab_entry;
346 cyg_mtab_entry *mte2 = cyg_cdir_mtab_entry;
347 cyg_dir dir1 = cyg_cdir_dir;
348 cyg_dir dir2 = cyg_cdir_dir;
349 const char *name1 = path1;
350 const char *name2 = path2;
352 ret = cyg_mtab_lookup( &dir1, &name1, &mte1 );
355 FILEIO_RETURN(ENOENT);
357 ret = cyg_mtab_lookup( &dir2, &name2, &mte2 );
360 FILEIO_RETURN(ENOENT);
362 // Cannot rename between different filesystems
364 FILEIO_RETURN(EXDEV);
368 ret = mte1->fs->rename( mte1, dir1, name1, dir2, name2 );
375 //==========================================================================
376 // Create a link from an existing file (path1) to a new one (path2)
378 __externC int link( const char *path1, const char *path2 )
383 cyg_mtab_entry *mte1 = cyg_cdir_mtab_entry;
384 cyg_mtab_entry *mte2 = cyg_cdir_mtab_entry;
385 cyg_dir dir1 = cyg_cdir_dir;
386 cyg_dir dir2 = cyg_cdir_dir;
387 const char *name1 = path1;
388 const char *name2 = path2;
390 ret = cyg_mtab_lookup( &dir1, &name1, &mte1 );
393 FILEIO_RETURN(ENOENT);
395 ret = cyg_mtab_lookup( &dir2, &name2, &mte2 );
398 FILEIO_RETURN(ENOENT);
400 // Cannot hard-link between different filesystems
402 FILEIO_RETURN(EXDEV);
406 ret = mte1->fs->link( mte1, dir1, name1, dir2, name2, CYG_FSLINK_HARD );
413 //==========================================================================
414 // Change current directory
416 __externC int chdir( const char *path )
421 cyg_mtab_entry *mte = cyg_cdir_mtab_entry;
422 cyg_dir dir = cyg_cdir_dir;
424 const char *name = path;
426 ret = cyg_mtab_lookup( &dir, &name, &mte );
429 FILEIO_RETURN(ENOENT);
433 ret = mte->fs->chdir( mte, dir, name, &newdir );
440 #ifdef CYGPKG_IO_FILEIO_TRACK_CWD
441 update_cwd( mte, dir, name );
444 if( cyg_cdir_mtab_entry != NULL && cyg_cdir_dir != CYG_DIR_NULL )
446 // Now detach from current cyg_cdir. We call the current directory's
447 // chdir routine with a NULL dir_out pointer.
449 LOCK_FS(cyg_cdir_mtab_entry);
451 ret = cyg_cdir_mtab_entry->fs->chdir( cyg_cdir_mtab_entry, cyg_cdir_dir, NULL, NULL );
453 UNLOCK_FS(cyg_cdir_mtab_entry);
455 // We really shouldn't get an error here.
460 cyg_cdir_mtab_entry = mte;
461 cyg_cdir_dir = newdir;
463 FILEIO_RETURN(ENOERR);
466 //==========================================================================
467 // Get file statistics
469 __externC int stat( const char *path, struct stat *buf )
474 cyg_mtab_entry *mte = cyg_cdir_mtab_entry;
475 cyg_dir dir = cyg_cdir_dir;
476 const char *name = path;
478 ret = cyg_mtab_lookup( &dir, &name, &mte );
481 FILEIO_RETURN(ENOENT);
485 ret = mte->fs->stat( mte, dir, name, buf );
492 //==========================================================================
493 // Get file configurable pathname variables
495 __externC long pathconf( const char *path, int vname )
500 cyg_mtab_entry *mte = cyg_cdir_mtab_entry;
501 cyg_dir dir = cyg_cdir_dir;
502 const char *name = path;
504 ret = cyg_mtab_lookup( &dir, &name, &mte );
507 FILEIO_RETURN(ENOENT);
509 struct cyg_pathconf_info info;
516 ret = mte->fs->getinfo( mte, dir, name,
517 FS_INFO_CONF, (char *)&info, sizeof(info) );
524 FILEIO_RETURN_VALUE(info.value);
527 //==========================================================================
528 // Sync filesystem without unmounting
530 __externC int cyg_fs_fssync( const char *path )
535 cyg_mtab_entry *mte = cyg_cdir_mtab_entry;
536 cyg_dir dir = cyg_cdir_dir;
537 const char *name = path;
539 ret = cyg_mtab_lookup( &dir, &name, &mte );
542 FILEIO_RETURN(ENOENT);
546 ret = mte->fs->setinfo( mte, dir, name, FS_INFO_SYNC, NULL, 0 );
553 FILEIO_RETURN_VALUE(ENOERR);
556 //==========================================================================
557 // Set file attributes
559 __externC int cyg_fs_set_attrib( const char *fname,
560 const cyg_fs_attrib_t new_attrib )
565 cyg_mtab_entry *mte = cyg_cdir_mtab_entry;
566 cyg_dir dir = cyg_cdir_dir;
567 const char *name = fname;
569 ret = cyg_mtab_lookup( &dir, &name, &mte );
572 FILEIO_RETURN(ENOENT);
576 ret = mte->fs->setinfo( mte, dir, name,
578 (char *)&new_attrib, sizeof(new_attrib) );
585 FILEIO_RETURN(ENOERR);
588 //==========================================================================
589 // Get file attributes
591 __externC int cyg_fs_get_attrib( const char *fname,
592 cyg_fs_attrib_t * const file_attrib )
597 cyg_mtab_entry *mte = cyg_cdir_mtab_entry;
598 cyg_dir dir = cyg_cdir_dir;
599 const char *name = fname;
601 ret = cyg_mtab_lookup( &dir, &name, &mte );
604 FILEIO_RETURN(ENOENT);
608 ret = mte->fs->getinfo( mte, dir, name,
610 (char *)file_attrib, sizeof(*file_attrib) );
617 FILEIO_RETURN(ENOERR);
620 //==========================================================================
621 // Access() function.
622 // This simply piggybacks onto stat().
624 extern int access(const char *path, int amode)
631 ret = stat( path, &buf );
633 // Translate not found into EACCES if the F_OK bit is
635 if( (amode & F_OK) && (ret < 0) && (errno == ENOENT) )
636 FILEIO_RETURN(EACCES);
638 // All other errors go straight back to the user.
640 FILEIO_RETURN_VALUE(ret);
642 // At present we do not have any access modes, so there is nothing
643 // to test. Just return success for all access modes.
645 FILEIO_RETURN(ENOERR);
648 //==========================================================================
651 __externC char *getcwd( char *buf, size_t size )
656 cyg_mtab_entry *mte = cyg_cdir_mtab_entry;
657 cyg_dir dir = cyg_cdir_dir;
658 cyg_getcwd_info info;
663 FILEIO_RETURN_VALUE(NULL);
671 err = mte->fs->getinfo( mte, dir, "",
672 FS_INFO_GETCWD, (char *)&info, sizeof(info) );
677 FILEIO_RETURN_VALUE(buf);
679 // Attempting to use filesystem support for getcwd() has
682 #ifdef CYGPKG_IO_FILEIO_TRACK_CWD
684 // If this option is set, the current directory path has been
685 // tracked in chdir(). Just report that value here.
687 if( size < cwd_size+1 )
690 FILEIO_RETURN_VALUE(NULL);
693 char *p = my_strcpy( buf, cwd );
698 // As a fallback we try to use ".." entries in the directory tree
699 // to climb back up to the root. Because we cannot assume that
700 // any filesystem can handle more than one directory pointer we
701 // have to do the climbing textually, by manufacturing a path name
702 // consisting of ".."s. At each level we then scan the parent
703 // directory looking for the entry for the lower level directory
704 // by matching st_ino values. This is not guaranteed to work at
705 // all since there is no requirement on filesystems to support "."
706 // and "..", or for them to report distinct inode values in
709 static char ddbuf[PATH_MAX];
710 char *p = buf+size-1;
713 // Claim lock to serialize use of ddbuf.
714 FILEIO_MUTEX_LOCK(getcwd_lock);
716 // Initialize ddbuf with ".".
721 // Start result buffer with a zero terminator. We accumulate the
722 // path name in the top end of the result buffer.
730 // Get status for "." and "..". If the filesystem does not
731 // support these, then this whole function will fail here.
733 err = stat( ddbuf, &sdbuf );
736 ddbuf[ddbufpos++] = '/';
737 ddbuf[ddbufpos++] = '.';
738 ddbuf[ddbufpos++] = '.';
739 ddbuf[ddbufpos] = '\0';
741 err = stat( ddbuf, &sddbuf );
744 // See whether we are at the root. This will be true when
745 // the inode numbers of "." and ".." are the same.
746 if( sdbuf.st_ino == sddbuf.st_ino )
749 // We now need to find an entry in the ".." directory that
750 // matches the inode number of ".".
753 DIR *d = opendir( ddbuf );
766 err = readdir_r( d, &de, &res );
767 if( err < 0 || res == NULL ) break;
769 // Skip "." and ".." entries.
770 if( pathcmp( de.d_name, "." ) || pathcmp( de.d_name, ".." ) )
773 // Tack the name of the directory entry on to the ddbuf
774 // and stat the object.
776 ddbuf[ddbufpos] = '/';
777 for( i = 0; de.d_name[i] != '\0'; i++ )
778 ddbuf[ddbufpos+i+1] = de.d_name[i];
779 ddbuf[ddbufpos+i+1] = '\0';
782 err = stat( ddbuf, &objstat );
785 // Cast out directories
786 if( !S_ISDIR(objstat.st_mode) )
789 // We have a directory. Compare its inode with that of "."
790 // and if they are the same, we have found our entry.
792 if( sdbuf.st_ino == objstat.st_ino )
796 ddbuf[ddbufpos] = '\0'; // reterminate ddbuf
800 // Halt on any errors.
804 // Here de contains the name of the directory entry in ".."
805 // that has the same inode as ".". Add the name to the path we
806 // are accumulating in the buffer.
809 while( *q != '\0' ) q++; // skip to end of name
814 } while( q != de.d_name );
816 *--p = '/'; // add a separator
819 // We have finished using ddbuf now.
820 FILEIO_MUTEX_UNLOCK(getcwd_lock);
823 FILEIO_RETURN_VALUE(NULL);
825 // We have the directory path in the top end of the buffer. Add
826 // the mount point name at the beginning and copy the rest of the
831 bp = my_strcpy( bp, mte->name );
833 // Sort out the separators between the mount name and the
834 // pathname. This is a bit messy since we have to deal with mount
835 // names of both "/" and "/foo" and pathnames that start with '/'
837 if( *(bp-1) != '/' && *p != '\0' ) *bp++ = '/';
840 // Now copy the path over.
844 *bp = '\0'; // Terminate the string
850 FILEIO_RETURN_VALUE(buf);
853 //==========================================================================
856 __externC int cyg_fs_getinfo( const char *path, int key, void *buf, int len )
861 cyg_mtab_entry *mte = cyg_cdir_mtab_entry;
862 cyg_dir dir = cyg_cdir_dir;
863 const char *name = path;
865 ret = cyg_mtab_lookup( &dir, &name, &mte );
868 FILEIO_RETURN(ENOENT);
872 ret = mte->fs->getinfo( mte, dir, name, key, buf, len );
879 //==========================================================================
882 __externC int cyg_fs_setinfo( const char *path, int key, void *buf, int len )
887 cyg_mtab_entry *mte = cyg_cdir_mtab_entry;
888 cyg_dir dir = cyg_cdir_dir;
889 const char *name = path;
891 ret = cyg_mtab_lookup( &dir, &name, &mte );
894 FILEIO_RETURN(ENOENT);
898 ret = mte->fs->setinfo( mte, dir, name, key, buf, len );
905 // -------------------------------------------------------------------------