/** * Parse an XML schema file and populate the priv->root virtual dentry tree * with the nodes found during the scanning. * @param filename XML input file. * @param idx LTFS index. * @param vol LTFS volume to which the index belongs. May be NULL. * @return 0 on success or a negative value on error. */ int xml_schema_from_file(const char *filename, struct ltfs_index *idx, struct ltfs_volume *vol) { int ret; xmlTextReaderPtr reader; xmlDocPtr doc; CHECK_ARG_NULL(filename, -LTFS_NULL_ARG); CHECK_ARG_NULL(idx, -LTFS_NULL_ARG); reader = xmlReaderForFile(filename, NULL, XML_PARSE_NOERROR | XML_PARSE_NOWARNING); if (! reader) { ltfsmsg(LTFS_ERR, "17011E", filename); return -1; } /* Workaround for old libxml2 version on OS X 10.5: the method used to preserve * unknown tags modifies the behavior of xmlFreeTextReader so that an additional * xmlDocFree call is required to free all memory. */ doc = xmlTextReaderCurrentDoc(reader); ret = _xml_parse_schema(reader, idx, vol); if (ret < 0) ltfsmsg(LTFS_ERR, "17012E", filename); if (doc) xmlFreeDoc(doc); xmlFreeTextReader(reader); #ifdef DEBUG /* dump the tree if it isn't too large */ if (ret == 0 && idx->file_count < 1000) fs_dump_tree(idx->root); #endif return ret; }
int dcache_force_to_sync(struct ltfs_volume *vol) { struct dcache_priv *priv = (struct dcache_priv *) vol ? vol->dcache_handle : NULL; CHECK_ARG_NULL(priv, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops->force_to_sync, -LTFS_NULL_ARG); return priv->ops->force_to_sync(priv->backend_handle); }
/** * Set the generation code into dcache space * @param gen Generation code to store into dcache space. * @param vol LTFS volume of the cartridge. Must have been initialized with dcache_init(). * @return 0 to indicate success or a negative value on error. */ int dcache_set_generation(unsigned int gen, struct ltfs_volume *vol) { struct dcache_priv *priv = (struct dcache_priv *) vol ? vol->dcache_handle : NULL; CHECK_ARG_NULL(vol, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops->set_generation, -LTFS_NULL_ARG); return priv->ops->set_generation(gen, priv->backend_handle); }
/** * Check if the loaded plugin is sharable or not. * @param sharable True is set if sharable * @return 0 to indicate success or a negative value on error. */ int dcache_is_sharable(bool *sharable, struct ltfs_volume *vol) { struct dcache_priv *priv = (struct dcache_priv *) vol ? vol->dcache_handle : NULL; CHECK_ARG_NULL(vol, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops->set_dirty, -LTFS_NULL_ARG); return priv->ops->is_sharable(sharable); }
/** * Unmount a disk image. * @param vol LTFS volume * @return 0 on success or a negative value on error. */ int dcache_diskimage_unmount(struct ltfs_volume *vol) { struct dcache_priv *priv = (struct dcache_priv *) vol ? vol->dcache_handle : NULL; CHECK_ARG_NULL(vol, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops->diskimage_unmount, -LTFS_NULL_ARG); return priv->ops->diskimage_unmount(priv->backend_handle); }
bool dcache_diskimage_is_full(struct ltfs_volume *vol) { struct dcache_priv *priv = (struct dcache_priv *) vol ? vol->dcache_handle : NULL; CHECK_ARG_NULL(vol, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops->diskimage_is_full, -LTFS_NULL_ARG); return priv->ops->diskimage_is_full(); }
/** * Set the Dentry cache 'dirty' flag. * @param dirty True to indicate that the disk-cache is out of sync with the latest LTFS Index, * False otherwise. * @param vol LTFS volume of the cartridge. Must have been initialized with dcache_init(). * @return 0 to indicate success or a negative value on error. */ int dcache_set_dirty(bool dirty, struct ltfs_volume *vol) { struct dcache_priv *priv = (struct dcache_priv *) vol ? vol->dcache_handle : NULL; CHECK_ARG_NULL(vol, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops->set_dirty, -LTFS_NULL_ARG); return priv->ops->set_dirty(dirty, priv->backend_handle); }
int dcache_put_dentry(struct dentry *d, struct ltfs_volume *vol) { struct dcache_priv *priv = (struct dcache_priv *) vol ? vol->dcache_handle : NULL; CHECK_ARG_NULL(d, -LTFS_NULL_ARG); CHECK_ARG_NULL(vol, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops->put_dentry, -LTFS_NULL_ARG); return priv->ops->put_dentry(d, priv->backend_handle); }
/** * Set the volume UUID. * @param uuid Volume UUID to store into dcache space. * @param vol LTFS volume of the cartridge. Must have been initialized with dcache_init(). * @return 0 to indicate success or a negative value on error. */ int dcache_set_vol_uuid(char *uuid, struct ltfs_volume *vol) { struct dcache_priv *priv = (struct dcache_priv *) vol ? vol->dcache_handle : NULL; CHECK_ARG_NULL(uuid, -LTFS_NULL_ARG); CHECK_ARG_NULL(vol, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops->set_vol_uuid, -LTFS_NULL_ARG); return priv->ops->set_vol_uuid(uuid, priv->backend_handle); }
/** * Get the volume UUID stored in dcache space. * @param work_dir Work directory. * @param barcode barcode * @param uuid Pointer to return the volume UUID. * @param vol LTFS volume of the cartridge. Must have been initialized with dcache_init(). * @return 0 to indicate success or a negative value on error. */ int dcache_get_vol_uuid(const char *work_dir, const char *barcode, char **uuid, struct ltfs_volume *vol) { struct dcache_priv *priv = (struct dcache_priv *) vol ? vol->dcache_handle : NULL; CHECK_ARG_NULL(uuid, -LTFS_NULL_ARG); CHECK_ARG_NULL(vol, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops->get_vol_uuid, -LTFS_NULL_ARG); return priv->ops->get_vol_uuid(work_dir, barcode, uuid); }
/** * Free in-memory dentry tree to reduce memory usage * @param vol LTFS volume of the cartridge. Must have been initialized with dcache_init(). * @return 0 to indicate success or a negative value on error. */ int dcache_wipe_dentry_tree(struct ltfs_volume *vol) { struct dcache_priv *priv = (struct dcache_priv *) vol ? vol->dcache_handle : NULL; CHECK_ARG_NULL(vol, -LTFS_NULL_ARG); CHECK_ARG_NULL(vol->index->root, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops->wipe_dentry_tree, -LTFS_NULL_ARG); return priv->ops->wipe_dentry_tree(priv->backend_handle); }
/** * Unload the Dentry cache. * @param vol LTFS volume of the cartridge. Must have been initialized with dcache_init(). * @return 0 to indicate success or a negative value on error. */ int dcache_unassign_name(struct ltfs_volume *vol) { struct dcache_priv *priv = (struct dcache_priv *) vol ? vol->dcache_handle : NULL; CHECK_ARG_NULL(vol, -LTFS_NULL_ARG); CHECK_ARG_NULL(vol->index, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops->unassign_name, -LTFS_NULL_ARG); return priv->ops->unassign_name(priv->backend_handle); }
/** * Get the Configure the Dentry cache work directory. * @param[out] workdir On success, contains a pointer to the LTFS work directory * @param vol LTFS volume * @return 0 to indicate success or a negative value on error. */ int dcache_get_workdir(char **workdir, struct ltfs_volume *vol) { struct dcache_priv *priv = (struct dcache_priv *) vol ? vol->dcache_handle : NULL; CHECK_ARG_NULL(workdir, -LTFS_NULL_ARG); CHECK_ARG_NULL(vol, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops->get_workdir, -LTFS_NULL_ARG); return priv->ops->get_workdir(workdir, priv->backend_handle); }
/** * Verify if the cache of a specific cartridge exists. * @param name Name of the cache to verify * @param[out] exists Outputs 'true' or 'false' after a successful call to this function. * @param[out] dirty If the cache exists, then contains the status of the cache's dirty flag. * @param vol LTFS volume * @return 0 on success or a negative value on error. */ int dcache_cache_exists(const char *name, bool *exists, struct ltfs_volume *vol) { struct dcache_priv *priv = (struct dcache_priv *) vol ? vol->dcache_handle : NULL; CHECK_ARG_NULL(name, -LTFS_NULL_ARG); CHECK_ARG_NULL(exists, -LTFS_NULL_ARG); CHECK_ARG_NULL(vol, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops->cache_exists, -LTFS_NULL_ARG); return priv->ops->cache_exists(name, exists, priv->backend_handle); }
/** * Get the generation code stored in dcache space. * @param work_dir Work directory. * @param barcode barcode * @param gen Pointer to return the generation code. * @param vol LTFS volume of the cartridge. Must have been initialized with dcache_init(). * @return 0 to indicate success or a negative value on error. */ int dcache_get_generation(const char *work_dir, const char *barcode, unsigned int *gen, struct ltfs_volume *vol) { struct dcache_priv *priv = (struct dcache_priv *) vol ? vol->dcache_handle : NULL; CHECK_ARG_NULL(gen, -LTFS_NULL_ARG); CHECK_ARG_NULL(vol, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops->get_generation, -LTFS_NULL_ARG); return priv->ops->get_generation(work_dir, barcode, gen); }
/** * Get the generation code. * @param work_dir Work directory. * @param barcode barcode * @param dirty Pointer to return the dirty flag. * @param vol LTFS volume of the cartridge. Must have been initialized with dcache_init(). * @return 0 to indicate success or a negative value on error. */ int dcache_get_dirty(const char *work_dir, const char *barcode, bool *dirty, struct ltfs_volume *vol) { struct dcache_priv *priv = (struct dcache_priv *) vol ? vol->dcache_handle : NULL; CHECK_ARG_NULL(dirty, -LTFS_NULL_ARG); CHECK_ARG_NULL(vol, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops->get_dirty, -LTFS_NULL_ARG); return priv->ops->get_dirty(work_dir, barcode, dirty); }
int dcache_close(struct dentry *d, bool lock_meta, bool descend, struct ltfs_volume *vol) { struct dcache_priv *priv = (struct dcache_priv *) vol ? vol->dcache_handle : NULL; CHECK_ARG_NULL(d, -LTFS_NULL_ARG); CHECK_ARG_NULL(vol, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops->close, -LTFS_NULL_ARG); return priv->ops->close(d, lock_meta, descend, priv->backend_handle); }
int dcache_put_advisory_lock(const char *name, struct ltfs_volume *vol) { struct dcache_priv *priv = (struct dcache_priv *) vol ? vol->dcache_handle : NULL; CHECK_ARG_NULL(name, -LTFS_NULL_ARG); CHECK_ARG_NULL(vol, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops->put_advisory_lock, -LTFS_NULL_ARG); return priv->ops->put_advisory_lock(name, priv->backend_handle); }
int dcache_flush(struct dentry *d, enum dcache_flush_flags flags, struct ltfs_volume *vol) { struct dcache_priv *priv = (struct dcache_priv *) vol ? vol->dcache_handle : NULL; CHECK_ARG_NULL(vol, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops->flush, -LTFS_NULL_ARG); if (! d) { /* The I/O scheduler handles NULL dentries in a special case. We just need to ignore them. */ return 0; } return priv->ops->flush(d, flags, priv->backend_handle); }
int xml_extent_symlink_info_from_file(const char *filename, struct dentry *d) { int ret; CHECK_ARG_NULL(filename, -LTFS_NULL_ARG); CHECK_ARG_NULL(d, -LTFS_NULL_ARG); ret = xml_extentlist_from_file(filename, d); if (d->realsize==0) { ret = xml_symlinkinfo_from_file(filename, d); } return ret; }
/** * Destroy the Dentry cache manager. * @param vol LTFS volume * @return 0 on success or a negative value on error. */ int dcache_destroy(struct ltfs_volume *vol) { struct dcache_priv *priv = (struct dcache_priv *) vol ? vol->dcache_handle : NULL; int ret; CHECK_ARG_NULL(vol, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops->destroy, -LTFS_NULL_ARG); ret = priv->ops->destroy(priv->backend_handle); vol->dcache_handle = NULL; free(priv); return ret; }
/** * 如果成功连接,返回socket,否则返回-1 */ int request_inner(char*web_host, int port) { int sockfd = -1; struct sockaddr_in host_addr; CHECK_ARG_NULL(web_host,-1,"argment host is null!\n"); //在请求域名解析时不加端口号 char*colon = strstr(web_host, ":"); (colon != NULL) ? *colon = '\0' : NULL;//先删除':' struct hostent* hostaddr = gethostbyname(web_host); (colon != NULL) ? *colon = ':' : NULL;//再还原':' if (hostaddr == NULL) { ERROR("request web host %s addr error,error=%s!\n",web_host,hstrerror(h_errno)); return -1; } if (hostaddr->h_addrtype != AF_INET) { ERROR("current only support AF_INET\n"); return -1; } if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { ERROR("create inet socket error,error=%s",strerror(errno)); return -1; } //初始化地址 bzero(&host_addr, sizeof(struct sockaddr_in)); host_addr.sin_family = AF_INET; host_addr.sin_port = htons(port); //尝试各种地址 char** addr_list = (hostaddr->h_addr_list); while (*addr_list != NULL) { bcopy(*addr_list, &(host_addr.sin_addr.s_addr), hostaddr->h_length); if (connect(sockfd, (struct sockaddr*) &host_addr, sizeof(struct sockaddr_in)) == 0) { break; } else { ERROR("connect %s(%s) error,error=%s\n",web_host,inet_ntoa(host_addr.sin_addr),strerror(errno)); addr_list++; } } if (addr_list == NULL) { return -1; } return sockfd; }
/** * Checks if the Dentry cache manager has been initialized for the given volume * @param vol LTFS volume * @return true to indicate that the Dentry cache manager has been initialized or false if not */ bool dcache_initialized(struct ltfs_volume *vol) { struct dcache_priv *priv = (struct dcache_priv *) vol ? vol->dcache_handle : NULL; bool assigned = false; /* The volume is passed as NULL always with the current implimentation */ if (! vol) return assigned; if (priv) { CHECK_ARG_NULL(priv->ops, false); CHECK_ARG_NULL(priv->ops->is_name_assigned, false); priv->ops->is_name_assigned(&assigned, priv->backend_handle); } return assigned; }
int dcache_init(struct libltfs_plugin *plugin, const struct dcache_options *options, struct ltfs_volume *vol) { struct dcache_priv *priv; unsigned int i; CHECK_ARG_NULL(plugin, -LTFS_NULL_ARG); CHECK_ARG_NULL(vol, -LTFS_NULL_ARG); priv = calloc(1, sizeof(struct dcache_priv)); if (! priv) { ltfsmsg(LTFS_ERR, "10001E", "dcache_init: private data"); return -LTFS_NO_MEMORY; } priv->plugin = plugin; priv->ops = plugin->ops; /* Verify that backend implements all required operations */ for (i=0; i<sizeof(struct dcache_ops)/sizeof(void *); ++i) { if (((void **)(priv->ops))[i] == NULL) { /* Dentry cache backend does not implement all required methods */ ltfsmsg(LTFS_ERR, "13004E"); free(priv); return -LTFS_PLUGIN_INCOMPLETE; } } priv->backend_handle = priv->ops->init(options, vol); if (! priv->backend_handle) { free(priv); return -1; } /* * dcache initialization can be performed at any time, even before the tape is mounted. * For that reason, the dcache_handle is attached to the LTFS Volume structure at this * point. We lend the dcache_handle to the LTFS Index structure on dcache_load(), which * must be called after the tape has been mounted. */ vol->dcache_handle = priv; return 0; }
int dcache_is_out_of_sync(bool *out_of_sync, struct ltfs_volume *vol) { struct dcache_priv *priv; CHECK_ARG_NULL(vol, -LTFS_NULL_ARG); CHECK_ARG_NULL(out_of_sync, -LTFS_NULL_ARG); if (! vol->dcache_handle) { /* vol->dcache_handle is set NULL, just after format or check */ *out_of_sync = false; return 0; } else priv = (struct dcache_priv *) vol->dcache_handle; CHECK_ARG_NULL(priv, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops->is_out_of_sync, -LTFS_NULL_ARG); return priv->ops->is_out_of_sync(out_of_sync, priv->backend_handle); }
int xml_label_from_file(const char *filename, struct ltfs_label *label) { int ret; xmlTextReaderPtr reader; CHECK_ARG_NULL(filename, -LTFS_NULL_ARG); CHECK_ARG_NULL(label, -LTFS_NULL_ARG); reader = xmlReaderForFile(filename, NULL, XML_PARSE_NOERROR | XML_PARSE_NOWARNING); if (! reader) { ltfsmsg(LTFS_ERR, "17007E", filename); return -1; } ret = _xml_parse_label(reader, label); if (ret < 0) ltfsmsg(LTFS_ERR, "17008E", filename); xmlFreeTextReader(reader); return ret; }
/** * Parse an extent list from a file and populate provided dentry with the extents read during * the scanning. * * @param filename File name from where to read the extent list from. * @param d Dentry where the extents are to be appended to. * @return 0 on success or a negative value on error. */ static int xml_extentlist_from_file(const char *filename, struct dentry *d) { declare_extent_parser_vars("extentinfo"); xmlTextReaderPtr reader; xmlDocPtr doc; int ret = 0; CHECK_ARG_NULL(filename, -LTFS_NULL_ARG); CHECK_ARG_NULL(d, -LTFS_NULL_ARG); reader = xmlReaderForFile(filename, NULL, XML_PARSE_NOERROR | XML_PARSE_NOWARNING); if (! reader) { ltfsmsg(LTFS_ERR, "17011E", filename); return -1; } /* Workaround for old libxml2 version on OS X 10.5: the method used to preserve * unknown tags modifies the behavior of xmlFreeTextReader so that an additional * xmlDocFree call is required to free all memory. */ doc = xmlTextReaderCurrentDoc(reader); while (true) { /* BEAM: loop doesn't iterate - Because get_next_tag() macro uses "break", at most once loop is needed here. */ get_next_tag(); if (! strcmp(name, "extentinfo")) { ret = _xml_parse_extents(reader, IDX_VERSION_SPARSE, d); if (ret < 0) { /* XML parser: failed to read extent list from file (%d) */ ltfsmsg(LTFS_ERR, "17084E", ret); } } break; } if (doc) xmlFreeDoc(doc); xmlFreeTextReader(reader); return ret; }
int dcache_setxattr(const char *path, struct dentry *d, const char *xattr, const char *value, size_t size, int flags, struct ltfs_volume *vol) { struct dcache_priv *priv = (struct dcache_priv *) vol ? vol->dcache_handle : NULL; CHECK_ARG_NULL(path, -LTFS_NULL_ARG); CHECK_ARG_NULL(d, -LTFS_NULL_ARG); CHECK_ARG_NULL(xattr, -LTFS_NULL_ARG); CHECK_ARG_NULL(value, -LTFS_NULL_ARG); CHECK_ARG_NULL(vol, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops->setxattr, -LTFS_NULL_ARG); return priv->ops->setxattr(path, d, xattr, value, size, flags, priv->backend_handle); }
int xml_label_from_mem(const char *buf, int buf_size, struct ltfs_label *label) { int ret; xmlTextReaderPtr reader; CHECK_ARG_NULL(buf, -LTFS_NULL_ARG); CHECK_ARG_NULL(label, -LTFS_NULL_ARG); reader = xmlReaderForMemory(buf, buf_size, NULL, NULL, XML_PARSE_NOERROR | XML_PARSE_NOWARNING); if (! reader) { ltfsmsg(LTFS_ERR, "17009E"); return -LTFS_LIBXML2_FAILURE; } ret = _xml_parse_label(reader, label); if (ret < 0) { ltfsmsg(LTFS_ERR, "17010E"); ret = -LTFS_LABEL_INVALID; } xmlFreeTextReader(reader); return ret; }
int dcache_openat(const char *parent_path, struct dentry *parent, const char *name, struct dentry **result, struct ltfs_volume *vol) { struct dcache_priv *priv = (struct dcache_priv *) vol ? vol->dcache_handle : NULL; CHECK_ARG_NULL(parent_path, -LTFS_NULL_ARG); CHECK_ARG_NULL(parent, -LTFS_NULL_ARG); CHECK_ARG_NULL(name, -LTFS_NULL_ARG); CHECK_ARG_NULL(result, -LTFS_NULL_ARG); CHECK_ARG_NULL(vol, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops, -LTFS_NULL_ARG); CHECK_ARG_NULL(priv->ops->openat, -LTFS_NULL_ARG); return priv->ops->openat(parent_path, parent, name, result, priv->backend_handle); }