123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053 |
- /*++
- Copyright (c) 2013 Minoca Corp.
- This file is licensed under the terms of the GNU General Public License
- version 3. Alternative licensing terms are available. Contact
- info@minocacorp.com for details. See the LICENSE file at the root of this
- project for complete licensing information.
- Module Name:
- dirio.c
- Abstract:
- This module implements directory enumeration functionality.
- Author:
- Evan Green 11-Mar-2013
- Environment:
- User Mode C Library
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include "libcp.h"
- #include <assert.h>
- #include <dirent.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/stat.h>
- //
- // ---------------------------------------------------------------- Definitions
- //
- #define DIRECTORY_BUFFER_SIZE 4096
- //
- // Define the initial guess for the current working directory buffer length.
- //
- #define WORKING_DIRECTORY_BUFFER_SIZE 256
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- /*++
- Structure Description:
- This structure stores information about about an open directory.
- Members:
- Descriptor - Stores the file descriptor number.
- Buffer - Stores a pointer to the buffer used to store several enumeration
- entries.
- ValidBufferSize - Stores the number of bytes in the buffer that actually
- contain valid data.
- CurrentPosition - Stores the offset within the buffer where the next entry
- will come from.
- AtEnd - Stores a boolean indicating if the end of the current buffer
- represents the end of the entire directory.
- Entry - Stores a pointer to the dirent structure expected at the final
- output.
- --*/
- typedef struct _DIR {
- HANDLE Descriptor;
- PVOID Buffer;
- ULONG ValidBufferSize;
- ULONG CurrentPosition;
- BOOL AtEnd;
- struct dirent Entry;
- } *PDIR;
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- PDIR
- ClpCreateDirectoryStructure (
- VOID
- );
- KSTATUS
- ClpDestroyDirectoryStructure (
- PDIR Directory
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- CHAR ClDirectoryEntryTypeConversions[IoObjectTypeCount] = {
- DT_UNKNOWN,
- DT_DIR,
- DT_REG,
- DT_BLK,
- DT_CHR,
- DT_FIFO,
- DT_DIR,
- DT_SOCK,
- DT_CHR,
- DT_CHR,
- DT_REG,
- DT_LNK
- };
- //
- // ------------------------------------------------------------------ Functions
- //
- LIBC_API
- DIR *
- opendir (
- const char *DirectoryName
- )
- /*++
- Routine Description:
- This routine opens a directory for reading.
- Arguments:
- DirectoryName - Supplies a pointer to a null terminated string containing
- the name of the directory to open.
- Return Value:
- Returns a pointer to the directory on success.
- NULL on failure.
- --*/
- {
- PDIR Directory;
- ULONG FilePathLength;
- ULONG Flags;
- KSTATUS Status;
- Directory = ClpCreateDirectoryStructure();
- if (Directory == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto opendirEnd;
- }
- FilePathLength = RtlStringLength((PSTR)DirectoryName);
- Flags = SYS_OPEN_FLAG_DIRECTORY | SYS_OPEN_FLAG_READ;
- Status = OsOpen(INVALID_HANDLE,
- (PSTR)DirectoryName,
- FilePathLength + 1,
- Flags,
- FILE_PERMISSION_NONE,
- &(Directory->Descriptor));
- if (!KSUCCESS(Status)) {
- goto opendirEnd;
- }
- opendirEnd:
- if (!KSUCCESS(Status)) {
- if (Directory != NULL) {
- ClpDestroyDirectoryStructure(Directory);
- Directory = NULL;
- }
- errno = ClConvertKstatusToErrorNumber(Status);
- }
- return Directory;
- }
- LIBC_API
- DIR *
- fdopendir (
- int FileDescriptor
- )
- /*++
- Routine Description:
- This routine opens a directory based on an already open file descriptor to
- a directory.
- Arguments:
- FileDescriptor - Supplies a pointer to the open handle to the directory.
- Return Value:
- 0 on success.
- -1 on failure, and more details will be provided in errno.
- --*/
- {
- PDIR Directory;
- KSTATUS Status;
- Directory = ClpCreateDirectoryStructure();
- if (Directory == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto fdopendirEnd;
- }
- Status = OsFileControl((HANDLE)(UINTN)FileDescriptor,
- FileControlCommandSetDirectoryFlag,
- NULL);
- if (!KSUCCESS(Status)) {
- goto fdopendirEnd;
- }
- Directory->Descriptor = (HANDLE)(UINTN)FileDescriptor;
- Status = STATUS_SUCCESS;
- fdopendirEnd:
- if (!KSUCCESS(Status)) {
- if (Directory != NULL) {
- ClpDestroyDirectoryStructure(Directory);
- Directory = NULL;
- }
- errno = ClConvertKstatusToErrorNumber(Status);
- }
- return Directory;
- }
- LIBC_API
- int
- closedir (
- DIR *Directory
- )
- /*++
- Routine Description:
- This routine closes an open directory.
- Arguments:
- Directory - Supplies a pointer to the structure returned by the open
- directory function.
- Return Value:
- 0 on success.
- -1 on failure, and more details will be provided in errno.
- --*/
- {
- KSTATUS Status;
- if (Directory == NULL) {
- Status = STATUS_SUCCESS;
- goto closedirEnd;
- }
- Status = ClpDestroyDirectoryStructure(Directory);
- closedirEnd:
- if (!KSUCCESS(Status)) {
- errno = ClConvertKstatusToErrorNumber(Status);
- return -1;
- }
- return 0;
- }
- LIBC_API
- int
- readdir_r (
- DIR *Directory,
- struct dirent *Buffer,
- struct dirent **Result
- )
- /*++
- Routine Description:
- This routine reads from a directory in a reentrant manner.
- Arguments:
- Directory - Supplies a pointer to the structure returned by the open
- directory function.
- Buffer - Supplies the buffer where the next directory entry will be
- returned.
- Result - Supplies a pointer that will either be set to the Buffer pointer
- if there are more entries, or NULL if there are no more entries in the
- directory.
- Return Value:
- 0 on success.
- Returns an error number on failure.
- --*/
- {
- UINTN BytesRead;
- PDIRECTORY_ENTRY Entry;
- INT Error;
- UINTN NextEntryOffset;
- KSTATUS Status;
- *Result = NULL;
- if (Directory == NULL) {
- Status = STATUS_INVALID_HANDLE;
- goto readdir_rEnd;
- }
- //
- // If more data needs to be read, perform the underlying read.
- //
- if (Directory->CurrentPosition + sizeof(DIRECTORY_ENTRY) >
- Directory->ValidBufferSize) {
- //
- // If this is the end, return a null entry and success.
- //
- if (Directory->AtEnd != FALSE) {
- Status = STATUS_SUCCESS;
- goto readdir_rEnd;
- }
- Status = OsPerformIo(Directory->Descriptor,
- IO_OFFSET_NONE,
- DIRECTORY_BUFFER_SIZE,
- 0,
- SYS_WAIT_TIME_INDEFINITE,
- Directory->Buffer,
- &BytesRead);
- if (!KSUCCESS(Status)) {
- goto readdir_rEnd;
- }
- if (BytesRead == 0) {
- Directory->AtEnd = TRUE;
- goto readdir_rEnd;
- }
- Directory->ValidBufferSize = (ULONG)BytesRead;
- Directory->CurrentPosition = 0;
- //
- // Make sure there is enough space for a new directory entry.
- //
- if (Directory->CurrentPosition + sizeof(DIRECTORY_ENTRY) >
- Directory->ValidBufferSize) {
- Status = STATUS_BUFFER_OVERRUN;
- goto readdir_rEnd;
- }
- }
- //
- // Grab the next directory entry.
- //
- Entry = (PDIRECTORY_ENTRY)(Directory->Buffer + Directory->CurrentPosition);
- NextEntryOffset = Directory->CurrentPosition + Entry->Size;
- if (NextEntryOffset > Directory->ValidBufferSize) {
- Status = STATUS_BUFFER_OVERRUN;
- goto readdir_rEnd;
- }
- if (Entry->Size - sizeof(DIRECTORY_ENTRY) > NAME_MAX) {
- Status = STATUS_BUFFER_OVERRUN;
- goto readdir_rEnd;
- }
- Buffer->d_ino = Entry->FileId;
- Buffer->d_off = Entry->NextOffset;
- Buffer->d_reclen = Entry->Size;
- //
- // Please update the array (and this assert) if a new I/O object type is
- // added.
- //
- assert(IoObjectSymbolicLink + 1 == IoObjectTypeCount);
- Buffer->d_type = ClDirectoryEntryTypeConversions[Entry->Type];
- RtlStringCopy((PSTR)&(Buffer->d_name), (PSTR)(Entry + 1), NAME_MAX);
- assert(strchr(Buffer->d_name, '/') == NULL);
- //
- // Move on to the next entry.
- //
- Directory->CurrentPosition = NextEntryOffset;
- Status = STATUS_SUCCESS;
- *Result = Buffer;
- readdir_rEnd:
- if (!KSUCCESS(Status)) {
- Error = ClConvertKstatusToErrorNumber(Status);
- } else {
- Error = 0;
- }
- return Error;
- }
- LIBC_API
- struct dirent *
- readdir (
- DIR *Directory
- )
- /*++
- Routine Description:
- This routine reads the next directory entry from the open directory
- stream.
- Arguments:
- Directory - Supplies a pointer to the structure returned by the open
- directory function.
- Return Value:
- Returns a pointer to the next directory entry on success.
- NULL on failure or when the end of the directory is reached. On failure,
- errno is set. If the end of the directory is reached, errno is not
- changed.
- --*/
- {
- int Error;
- struct dirent *NextEntry;
- Error = readdir_r(Directory, &(Directory->Entry), &NextEntry);
- if (Error != 0) {
- errno = Error;
- }
- return NextEntry;
- }
- LIBC_API
- void
- seekdir (
- DIR *Directory,
- long Location
- )
- /*++
- Routine Description:
- This routine seeks directory to the given location. The location must have
- been returned from a previous call to telldir, otherwise the results are
- undefined.
- Arguments:
- Directory - Supplies a pointer to the structure returned by the open
- directory function.
- Location - Supplies the location within the directory to seek to as given
- by the telldir function.
- Return Value:
- None. No errors are defined.
- --*/
- {
- OsSeek(Directory->Descriptor,
- SeekCommandFromBeginning,
- Location,
- NULL);
- Directory->ValidBufferSize = 0;
- Directory->CurrentPosition = 0;
- Directory->AtEnd = FALSE;
- return;
- }
- LIBC_API
- long
- telldir (
- DIR *Directory
- )
- /*++
- Routine Description:
- This routine returns the current position within a directory. This position
- can be seeked to later (in fact, the return value from this function is the
- only valid parameter to pass to seekdir).
- Arguments:
- Directory - Supplies a pointer to the structure returned by the open
- directory function.
- Return Value:
- Returns the current location of the specified directory stream.
- --*/
- {
- IO_OFFSET Offset;
- KSTATUS Status;
- Status = OsSeek(Directory->Descriptor,
- SeekCommandNop,
- 0,
- &Offset);
- if (!KSUCCESS(Status)) {
- return 0;
- }
- return (long)Offset;
- }
- LIBC_API
- void
- rewinddir (
- DIR *Directory
- )
- /*++
- Routine Description:
- This routine rewinds a directory back to the beginning.
- Arguments:
- Directory - Supplies a pointer to the structure returned by the open
- directory function.
- Return Value:
- None.
- --*/
- {
- seekdir(Directory, 0);
- return;
- }
- LIBC_API
- int
- dirfd (
- DIR *Directory
- )
- /*++
- Routine Description:
- This routine returns the file descriptor backing the given directory.
- Arguments:
- Directory - Supplies a pointer to the structure returned by the open
- directory function.
- Return Value:
- None.
- --*/
- {
- if ((Directory == NULL) || (Directory->Descriptor == INVALID_HANDLE)) {
- errno = EINVAL;
- return -1;
- }
- return (int)(UINTN)(Directory->Descriptor);
- }
- LIBC_API
- int
- rmdir (
- const char *Path
- )
- /*++
- Routine Description:
- This routine attempts to unlink a directory. The directory must be empty or
- the operation will fail.
- Arguments:
- Path - Supplies a pointer to a null terminated string containing the path
- of the directory to remove.
- Return Value:
- 0 on success.
- -1 on failure, and errno will be set to provide more details. In failure
- cases, the directory will not be removed.
- --*/
- {
- return unlinkat(AT_FDCWD, Path, AT_REMOVEDIR);
- }
- LIBC_API
- char *
- getcwd (
- char *Buffer,
- size_t BufferSize
- )
- /*++
- Routine Description:
- This routine returns a pointer to a null terminated string containing the
- path to the current working directory.
- Arguments:
- Buffer - Supplies a pointer to a buffer where the string should be returned.
- If NULL is supplied, then malloc will be used to allocate a buffer of
- the appropriate size, and it is therefore the caller's responsibility
- to free this memory.
- BufferSize - Supplies the size of the buffer, in bytes.
- Return Value:
- Returns a pointer to a string containing the current working directory on
- success.
- NULL on failure. Errno will contain more information.
- --*/
- {
- PSTR Directory;
- UINTN DirectorySize;
- KSTATUS Status;
- Status = OsGetCurrentDirectory(FALSE, &Directory, &DirectorySize);
- if (!KSUCCESS(Status)) {
- errno = ClConvertKstatusToErrorNumber(Status);
- return NULL;
- }
- //
- // If a buffer was supplied, try to use it.
- //
- if (Buffer != NULL) {
- //
- // The size argument is required if a buffer was supplied.
- //
- if (BufferSize == 0) {
- errno = EINVAL;
- Buffer = NULL;
- //
- // If a size was supplied but is too small, notify the caller.
- //
- } else if (BufferSize < DirectorySize) {
- errno = ERANGE;
- Buffer = NULL;
- //
- // Otherwise copy the current directory into the supplied buffer.
- //
- } else {
- memcpy(Buffer, Directory, DirectorySize);
- }
- //
- // If there is no provided buffer, then allocate a buffer of the
- // appropriate size and copy the directory into it. Ignore the supplied
- // buffer size.
- //
- } else {
- Buffer = malloc(DirectorySize);
- if (Buffer != NULL) {
- memcpy(Buffer, Directory, DirectorySize);
- } else {
- errno = ENOMEM;
- }
- }
- //
- // Free the allocated directory and return the user's buffer.
- //
- if (Directory != NULL) {
- OsHeapFree(Directory);
- Directory = NULL;
- }
- return Buffer;
- }
- LIBC_API
- int
- chdir (
- const char *Path
- )
- /*++
- Routine Description:
- This routine changes the current working directory (the starting point for
- all paths that don't begin with a path separator).
- Arguments:
- Path - Supplies a pointer to the null terminated string containing the
- path of the new working directory.
- Return Value:
- 0 on success.
- -1 on failure, and errno will be set to provide more details. On failure,
- the current working directory will not be changed.
- --*/
- {
- ULONG PathSize;
- KSTATUS Status;
- if (Path == NULL) {
- errno = EINVAL;
- return -1;
- }
- PathSize = strlen((PSTR)Path) + 1;
- Status = OsChangeDirectory(FALSE, (PSTR)Path, PathSize);
- if (!KSUCCESS(Status)) {
- errno = ClConvertKstatusToErrorNumber(Status);
- return -1;
- }
- return 0;
- }
- LIBC_API
- int
- fchdir (
- int FileDescriptor
- )
- /*++
- Routine Description:
- This routine changes the current working directory (the starting point for
- all paths that don't begin with a path separator) using an already open
- file descriptor to that directory.
- Arguments:
- FileDescriptor - Supplies the open file handle to the directory to change
- to.
- Return Value:
- 0 on success.
- -1 on failure, and errno will be set to provide more details. On failure,
- the current working directory will not be changed.
- --*/
- {
- KSTATUS Status;
- Status = OsChangeDirectoryHandle(FALSE, (HANDLE)(UINTN)FileDescriptor);
- if (!KSUCCESS(Status)) {
- errno = ClConvertKstatusToErrorNumber(Status);
- return -1;
- }
- return 0;
- }
- LIBC_API
- int
- chroot (
- const char *Path
- )
- /*++
- Routine Description:
- This routine changes the current root directory. The working directory is
- not changed. The caller must have sufficient privileges to change root
- directories.
- Arguments:
- Path - Supplies a pointer to the null terminated string containing the
- path of the new root directory.
- Return Value:
- 0 on success.
- -1 on failure, and errno will be set to provide more details. On failure,
- the current root directory will not be changed. Errno may be set to the
- following values, among others:
- EACCES if search permission is denied on a component of the path prefix.
- EPERM if the caller has insufficient privileges.
- --*/
- {
- ULONG PathSize;
- KSTATUS Status;
- //
- // This is a Minoca extension. If the caller passes NULL, then this routine
- // will try to escape the current root. This is only possible if the caller
- // has the permission to escape roots.
- //
- if (Path == NULL) {
- Status = OsChangeDirectory(TRUE, NULL, 0);
- } else {
- PathSize = strlen((PSTR)Path) + 1;
- Status = OsChangeDirectory(TRUE, (PSTR)Path, PathSize);
- }
- if (!KSUCCESS(Status)) {
- errno = ClConvertKstatusToErrorNumber(Status);
- return -1;
- }
- return 0;
- }
- LIBC_API
- int
- fchroot (
- int FileDescriptor
- )
- /*++
- Routine Description:
- This routine changes the current root directory using an already open file
- descriptor to that directory. The caller must have sufficient privileges
- to change root directories.
- Arguments:
- FileDescriptor - Supplies the open file handle to the directory to change
- to.
- Return Value:
- 0 on success.
- -1 on failure, and errno will be set to provide more details. On failure,
- the current root directory will not be changed.
- --*/
- {
- KSTATUS Status;
- Status = OsChangeDirectoryHandle(TRUE, (HANDLE)(UINTN)FileDescriptor);
- if (!KSUCCESS(Status)) {
- errno = ClConvertKstatusToErrorNumber(Status);
- return -1;
- }
- return 0;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- PDIR
- ClpCreateDirectoryStructure (
- VOID
- )
- /*++
- Routine Description:
- This routine creates a directory structure.
- Arguments:
- None.
- Return Value:
- Returns a pointer to the directory structure on success.
- NULL on allocation failure.
- --*/
- {
- PDIR Directory;
- KSTATUS Status;
- Status = STATUS_INSUFFICIENT_RESOURCES;
- Directory = malloc(sizeof(DIR));
- if (Directory == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto CreateDirectoryStructureEnd;
- }
- RtlZeroMemory(Directory, sizeof(DIR));
- Directory->Descriptor = INVALID_HANDLE;
- Directory->Buffer = malloc(DIRECTORY_BUFFER_SIZE + 1);
- if (Directory->Buffer == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto CreateDirectoryStructureEnd;
- }
- Status = STATUS_SUCCESS;
- CreateDirectoryStructureEnd:
- if (!KSUCCESS(Status)) {
- if (Directory != NULL) {
- ClpDestroyDirectoryStructure(Directory);
- Directory = NULL;
- }
- }
- return Directory;
- }
- KSTATUS
- ClpDestroyDirectoryStructure (
- PDIR Directory
- )
- /*++
- Routine Description:
- This routine destroys a directory structure.
- Arguments:
- Directory - Supplies a pointer to the directory structure to destroy.
- Return Value:
- Returns the resulting status code from the close function.
- --*/
- {
- KSTATUS Status;
- Status = STATUS_INVALID_HANDLE;
- if (Directory->Descriptor != INVALID_HANDLE) {
- Status = OsClose(Directory->Descriptor);
- }
- if (Directory->Buffer != NULL) {
- free(Directory->Buffer);
- }
- free(Directory);
- return Status;
- }
|