/** * Performs a file open operation. * * @param out_file On success, a pointer to the newly-created file * handle gets written here. * @param path The path of the file to open. * @param access_flags Flags controlling file access; see nffs_open() for * details. * * @return 0 on success; nonzero on failure. */ int nffs_file_open(struct nffs_file **out_file, const char *path, uint8_t access_flags) { struct nffs_path_parser parser; struct nffs_inode_entry *parent; struct nffs_inode_entry *inode; struct nffs_file *file; int rc; file = NULL; /* Reject invalid access flag combinations. */ if (!(access_flags & (FS_ACCESS_READ | FS_ACCESS_WRITE))) { rc = FS_EINVAL; goto err; } if (access_flags & (FS_ACCESS_APPEND | FS_ACCESS_TRUNCATE) && !(access_flags & FS_ACCESS_WRITE)) { rc = FS_EINVAL; goto err; } if (access_flags & FS_ACCESS_APPEND && access_flags & FS_ACCESS_TRUNCATE) { rc = FS_EINVAL; goto err; } file = nffs_file_alloc(); if (file == NULL) { rc = FS_ENOMEM; goto err; } nffs_path_parser_new(&parser, path); rc = nffs_path_find(&parser, &inode, &parent); if (rc == FS_ENOENT && parser.npp_token_type == NFFS_PATH_TOKEN_LEAF) { /* The path is valid, but the file does not exist. This is an error * for read-only opens. */ if (!(access_flags & FS_ACCESS_WRITE)) { goto err; } /* Make sure the parent directory exists. */ if (parent == NULL) { goto err; } /* Create a new file at the specified path. */ rc = nffs_file_new(parent, parser.npp_token, parser.npp_token_len, 0, &file->nf_inode_entry); if (rc != 0) { goto err; } } else if (rc == 0) { /* The file already exists. */ /* Reject an attempt to open a directory. */ if (nffs_hash_id_is_dir(inode->nie_hash_entry.nhe_id)) { rc = FS_EINVAL; goto err; } if (access_flags & FS_ACCESS_TRUNCATE) { /* The user is truncating the file. Unlink the old file and create * a new one in its place. */ nffs_path_unlink(path); rc = nffs_file_new(parent, parser.npp_token, parser.npp_token_len, 0, &file->nf_inode_entry); if (rc != 0) { goto err; } } else { /* The user is not truncating the file. Point the file handle to * the existing inode. */ file->nf_inode_entry = inode; } } else { /* Invalid path. */ goto err; } if (access_flags & FS_ACCESS_APPEND) { rc = nffs_inode_data_len(file->nf_inode_entry, &file->nf_offset); if (rc != 0) { goto err; } } else { file->nf_offset = 0; } file->nf_inode_entry->nie_refcnt++; file->nf_access_flags = access_flags; *out_file = file; return 0; err: nffs_file_free(file); return rc; }
/** * Erases all the specified areas and initializes them with a clean nffs * file system. * * @param area_descs The set of areas to format. * * @return 0 on success; * nonzero on failure. */ int nffs_format_full(const struct nffs_area_desc *area_descs) { int rc; int i; /* Start from a clean state. */ nffs_misc_reset(); /* Select largest area to be the initial scratch area. */ nffs_scratch_area_idx = 0; for (i = 1; area_descs[i].nad_length != 0; i++) { if (i >= NFFS_MAX_AREAS) { rc = FS_EINVAL; goto err; } if (area_descs[i].nad_length > area_descs[nffs_scratch_area_idx].nad_length) { nffs_scratch_area_idx = i; } } rc = nffs_misc_set_num_areas(i); if (rc != 0) { goto err; } for (i = 0; i < nffs_num_areas; i++) { nffs_areas[i].na_offset = area_descs[i].nad_offset; nffs_areas[i].na_length = area_descs[i].nad_length; nffs_areas[i].na_flash_id = area_descs[i].nad_flash_id; nffs_areas[i].na_cur = 0; nffs_areas[i].na_gc_seq = 0; if (i == nffs_scratch_area_idx) { nffs_areas[i].na_id = NFFS_AREA_ID_NONE; } else { nffs_areas[i].na_id = i; } rc = nffs_format_area(i, i == nffs_scratch_area_idx); if (rc != 0) { goto err; } } rc = nffs_misc_validate_scratch(); if (rc != 0) { goto err; } /* Create root directory. */ rc = nffs_file_new(NULL, "", 0, 1, &nffs_root_dir); if (rc != 0) { goto err; } /* Create "lost+found" directory. */ rc = nffs_misc_create_lost_found_dir(); if (rc != 0) { goto err; } rc = nffs_misc_validate_root_dir(); if (rc != 0) { goto err; } rc = nffs_misc_set_max_block_data_len(0); if (rc != 0) { goto err; } return 0; err: nffs_misc_reset(); return rc; }