EFI_STATUS fsw_efi_dnode_fill_FileInfo(IN FSW_VOLUME_DATA *Volume, IN struct fsw_dnode *dno, IN OUT UINTN *BufferSize, OUT VOID *Buffer) { EFI_STATUS Status; EFI_FILE_INFO *FileInfo; UINTN RequiredSize; struct fsw_dnode_stat sb; // make sure the dnode has complete info Status = fsw_efi_map_status(fsw_dnode_fill(dno), Volume); if (EFI_ERROR(Status)) return Status; // TODO: check/assert that the dno's name is in UTF16 // check buffer size RequiredSize = SIZE_OF_EFI_FILE_INFO + fsw_efi_strsize(&dno->name); if (*BufferSize < RequiredSize) { // TODO: wind back the directory in this case #if DEBUG_LEVEL Print(L"...BUFFER TOO SMALL\n"); #endif *BufferSize = RequiredSize; return EFI_BUFFER_TOO_SMALL; } // fill structure ZeroMem(Buffer, RequiredSize); FileInfo = (EFI_FILE_INFO *)Buffer; FileInfo->Size = RequiredSize; FileInfo->FileSize = dno->size; FileInfo->Attribute = 0; if (dno->type == FSW_DNODE_TYPE_DIR) FileInfo->Attribute |= EFI_FILE_DIRECTORY; fsw_efi_strcpy(FileInfo->FileName, &dno->name); // get the missing info from the fs driver ZeroMem(&sb, sizeof(struct fsw_dnode_stat)); sb.store_time_posix = fsw_efi_store_time_posix; sb.store_attr_posix = fsw_efi_store_attr_posix; sb.host_data = FileInfo; Status = fsw_efi_map_status(fsw_dnode_stat(dno, &sb), Volume); if (EFI_ERROR(Status)) return Status; FileInfo->PhysicalSize = sb.used_bytes; // prepare for return *BufferSize = RequiredSize; #if DEBUG_LEVEL Print(L"...returning '%s'\n", FileInfo->FileName); #endif return EFI_SUCCESS; }
EFI_STATUS fsw_efi_dnode_to_FileHandle(IN struct fsw_dnode *dno, OUT EFI_FILE **NewFileHandle) { EFI_STATUS Status; FSW_FILE_DATA *File; FSW_MSG_DEBUG((FSW_MSGSTR(__FUNCTION__ ": enter\n"))); // make sure the dnode has complete info Status = fsw_efi_map_status(fsw_dnode_fill(dno), (FSW_VOLUME_DATA *)dno->vol->host_data); if (EFI_ERROR(Status)) { goto Done; } // check type if (dno->type != FSW_DNODE_TYPE_FILE && dno->type != FSW_DNODE_TYPE_DIR) { Status = EFI_UNSUPPORTED; goto Done; } // allocate file structure File = AllocateZeroPool(sizeof(FSW_FILE_DATA)); File->Signature = FSW_FILE_DATA_SIGNATURE; if (dno->type == FSW_DNODE_TYPE_FILE) File->Type = FSW_EFI_FILE_TYPE_FILE; else if (dno->type == FSW_DNODE_TYPE_DIR) File->Type = FSW_EFI_FILE_TYPE_DIR; // open shandle Status = fsw_efi_map_status(fsw_shandle_open(dno, &File->shand), (FSW_VOLUME_DATA *)dno->vol->host_data); if (EFI_ERROR(Status)) { FreePool(File); goto Done; } // populate the file handle File->FileHandle.Revision = EFI_FILE_HANDLE_REVISION; File->FileHandle.Open = fsw_efi_FileHandle_Open; File->FileHandle.Close = fsw_efi_FileHandle_Close; File->FileHandle.Delete = fsw_efi_FileHandle_Delete; File->FileHandle.Read = fsw_efi_FileHandle_Read; File->FileHandle.Write = fsw_efi_FileHandle_Write; File->FileHandle.GetPosition = fsw_efi_FileHandle_GetPosition; File->FileHandle.SetPosition = fsw_efi_FileHandle_SetPosition; File->FileHandle.GetInfo = fsw_efi_FileHandle_GetInfo; File->FileHandle.SetInfo = fsw_efi_FileHandle_SetInfo; File->FileHandle.Flush = fsw_efi_FileHandle_Flush; *NewFileHandle = &File->FileHandle; Status = EFI_SUCCESS; Done: FSW_MSG_DEBUG((FSW_MSGSTR(__FUNCTION__ ": leaving with %r\n"), Status)); return Status; }
fsw_status_t fsw_dnode_readlink(struct fsw_dnode *dno, struct fsw_string *target_name) { fsw_status_t status; status = fsw_dnode_fill(dno); if (status) return status; if (dno->type != FSW_DNODE_TYPE_SYMLINK) return FSW_UNSUPPORTED; return dno->vol->fstype_table->readlink(dno->vol, dno, target_name); }
fsw_status_t fsw_dnode_lookup(struct fsw_dnode *dno, struct fsw_string *lookup_name, struct fsw_dnode **child_dno_out) { fsw_status_t status; status = fsw_dnode_fill(dno); if (status) return status; if (dno->type != FSW_DNODE_TYPE_DIR) return FSW_UNSUPPORTED; return dno->vol->fstype_table->dir_lookup(dno->vol, dno, lookup_name, child_dno_out); }
fsw_status_t fsw_dnode_stat(struct fsw_dnode *dno, struct fsw_dnode_stat *sb) { fsw_status_t status; status = fsw_dnode_fill(dno); if (status) return status; sb->used_bytes = 0; status = dno->vol->fstype_table->dnode_stat(dno->vol, dno, sb); if (!status && !sb->used_bytes) sb->used_bytes = FSW_U64_DIV(dno->size + dno->vol->log_blocksize - 1, dno->vol->log_blocksize); return status; }
fsw_status_t fsw_posix_open_dno(struct fsw_posix_volume *pvol, const char *path, int required_type, struct fsw_shandle *shand) { fsw_status_t status; struct fsw_dnode *dno; struct fsw_dnode *target_dno; struct fsw_string lookup_path; lookup_path.type = FSW_STRING_TYPE_ISO88591; lookup_path.len = strlen(path); lookup_path.size = lookup_path.len; lookup_path.data = (void *)path; // resolve the path (symlinks along the way are automatically resolved) status = fsw_dnode_lookup_path(pvol->vol->root, &lookup_path, '/', &dno); if (status) { fprintf(stderr, "fsw_posix_open_dno: fsw_dnode_lookup_path returned %d\n", status); return status; } // if the final node is a symlink, also resolve it status = fsw_dnode_resolve(dno, &target_dno); fsw_dnode_release(dno); if (status) { fprintf(stderr, "fsw_posix_open_dno: fsw_dnode_resolve returned %d\n", status); return status; } dno = target_dno; // check that it is a regular file status = fsw_dnode_fill(dno); if (status) { fprintf(stderr, "fsw_posix_open_dno: fsw_dnode_fill returned %d\n", status); fsw_dnode_release(dno); return status; } if (dno->type != required_type) { fprintf(stderr, "fsw_posix_open_dno: dnode is not of the requested type\n"); fsw_dnode_release(dno); return FSW_UNSUPPORTED; } // open shandle status = fsw_shandle_open(dno, shand); if (status) { fprintf(stderr, "fsw_posix_open_dno: fsw_shandle_open returned %d\n", status); } fsw_dnode_release(dno); return status; }
fsw_status_t fsw_dnode_resolve(struct fsw_dnode *dno, struct fsw_dnode **target_dno_out) { fsw_status_t status; struct fsw_string target_name; struct fsw_dnode *target_dno; /* Linux kernel max link count is 40 */ int link_count = 40; fsw_dnode_retain(dno); while (--link_count > 0) { // get full information status = fsw_dnode_fill(dno); if (status) goto errorexit; if (dno->type != FSW_DNODE_TYPE_SYMLINK) { // found a non-symlink target, return it *target_dno_out = dno; return FSW_SUCCESS; } if (dno->parent == NULL) { // safety measure, cannot happen in theory status = FSW_NOT_FOUND; goto errorexit; } // read the link's target status = fsw_dnode_readlink(dno, &target_name); if (status) goto errorexit; // resolve it status = fsw_dnode_lookup_path(dno->parent, &target_name, '/', &target_dno); fsw_strfree(&target_name); if (status) goto errorexit; // target_dno becomes the new dno fsw_dnode_release(dno); dno = target_dno; // is already retained } if(link_count == 0) status = FSW_NOT_FOUND; errorexit: fsw_dnode_release(dno); return status; }
struct dirent * fsw_posix_readdir(struct fsw_posix_dir *dir) { fsw_status_t status; struct fsw_dnode *dno; static struct dirent dent; // get next entry from file system status = fsw_dnode_dir_read(&dir->shand, &dno); if (status) { fprintf(stderr, "fsw_posix_readdir: fsw_dnode_dir_read returned %d\n", status); return NULL; } status = fsw_dnode_fill(dno); if (status) { fprintf(stderr, "fsw_posix_readdir: fsw_dnode_fill returned %d\n", status); fsw_dnode_release(dno); return NULL; } // fill dirent structure dent.d_fileno = dno->dnode_id; dent.d_reclen = 8 + dno->name.size + 1; switch (dno->type) { case FSW_DNODE_TYPE_FILE: dent.d_type = DT_REG; break; case FSW_DNODE_TYPE_DIR: dent.d_type = DT_DIR; break; case FSW_DNODE_TYPE_SYMLINK: dent.d_type = DT_LNK; break; default: dent.d_type = DT_UNKNOWN; break; } dent.d_namlen = dno->name.size; memcpy(dent.d_name, dno->name.data, dno->name.size); dent.d_name[dent.d_namlen] = 0; return &dent; }
fsw_status_t fsw_dnode_lookup_path(struct fsw_dnode *dno, struct fsw_string *lookup_path, char separator, struct fsw_dnode **child_dno_out) { fsw_status_t status; struct fsw_volume *vol = dno->vol; struct fsw_dnode *child_dno = NULL; struct fsw_string lookup_name; struct fsw_string remaining_path; int root_if_empty; remaining_path = *lookup_path; fsw_dnode_retain(dno); // loop over the path for (root_if_empty = 1; fsw_strlen(&remaining_path) > 0; root_if_empty = 0) { // parse next path component fsw_strsplit(&lookup_name, &remaining_path, separator); FSW_MSG_DEBUG((FSW_MSGSTR("fsw_dnode_lookup_path: split into %d '%s' and %d '%s'\n"), lookup_name.len, lookup_name.data, remaining_path.len, remaining_path.data)); if (fsw_strlen(&lookup_name) == 0) { // empty path component if (root_if_empty) child_dno = vol->root; else child_dno = dno; fsw_dnode_retain(child_dno); } else { // do an actual directory lookup // ensure we have full information status = fsw_dnode_fill(dno); if (status) goto errorexit; // resolve symlink if necessary if (dno->type == FSW_DNODE_TYPE_SYMLINK) { status = fsw_dnode_resolve(dno, &child_dno); if (status) goto errorexit; // symlink target becomes the new dno fsw_dnode_release(dno); dno = child_dno; // is already retained child_dno = NULL; // ensure we have full information status = fsw_dnode_fill(dno); if (status) goto errorexit; } // make sure we operate on a directory if (dno->type != FSW_DNODE_TYPE_DIR) { return FSW_UNSUPPORTED; goto errorexit; } // check special paths if (fsw_streq_cstr(&lookup_name, ".")) { // self directory child_dno = dno; fsw_dnode_retain(child_dno); } else if (fsw_streq_cstr(&lookup_name, "..")) { // parent directory if (dno->parent == NULL) { // We cannot go up from the root directory. Caution: Certain apps like the EFI shell // rely on this behaviour! status = FSW_NOT_FOUND; goto errorexit; } child_dno = dno->parent; fsw_dnode_retain(child_dno); } else { // do an actual lookup status = vol->fstype_table->dir_lookup(vol, dno, &lookup_name, &child_dno); if (status) goto errorexit; } } // child_dno becomes the new dno fsw_dnode_release(dno); dno = child_dno; // is already retained child_dno = NULL; FSW_MSG_DEBUG((FSW_MSGSTR("fsw_dnode_lookup_path: now at inode %d\n"), dno->dnode_id)); } *child_dno_out = dno; return FSW_SUCCESS; errorexit: FSW_MSG_DEBUG((FSW_MSGSTR("fsw_dnode_lookup_path: leaving with error %d\n"), status)); fsw_dnode_release(dno); if (child_dno != NULL) fsw_dnode_release(child_dno); return status; }