fsw_status_t fsw_dnode_create_with_tree(struct fsw_dnode *parent_dno, fsw_u64 tree_id, fsw_u64 dnode_id, int type, struct fsw_string *name, struct fsw_dnode **dno_out) { fsw_status_t status; struct fsw_volume *vol = parent_dno->vol; struct fsw_dnode *dno; // check if we already have a dnode with the same id for (dno = vol->dnode_head; dno; dno = dno->next) { if (dno->dnode_id == dnode_id && dno->tree_id == tree_id) { fsw_dnode_retain(dno); *dno_out = dno; return FSW_SUCCESS; } } // allocate memory for the structure status = fsw_alloc_zero(vol->fstype_table->dnode_struct_size, (void **)&dno); if (status) return status; // fill the structure dno->vol = vol; dno->parent = parent_dno; fsw_dnode_retain(dno->parent); dno->tree_id = tree_id; dno->dnode_id = dnode_id; dno->type = type; dno->refcount = 1; status = fsw_strdup_coerce(&dno->name, vol->host_table->native_string_type, name); if (status) { fsw_free(dno); return status; } fsw_dnode_register(vol, dno); *dno_out = dno; return FSW_SUCCESS; }
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; }
fsw_status_t fsw_shandle_open(struct fsw_dnode *dno, struct fsw_shandle *shand) { fsw_status_t status; struct fsw_volume *vol = dno->vol; // read full dnode information into memory status = vol->fstype_table->dnode_fill(vol, dno); if (status) return status; // setup shandle fsw_dnode_retain(dno); shand->dnode = dno; shand->pos = 0; shand->extent.type = FSW_EXTENT_TYPE_INVALID; return FSW_SUCCESS; }
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; }