static int virStorageBackendISCSIStartPool(virConnectPtr conn ATTRIBUTE_UNUSED, virStoragePoolObjPtr pool) { char *portal = NULL; char *session; if (pool->def->source.host.name == NULL) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing source host")); return -1; } if (pool->def->source.ndevice != 1 || pool->def->source.devices[0].path == NULL) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing source device")); return -1; } if ((session = virStorageBackendISCSISession(pool, 1)) == NULL) { if ((portal = virStorageBackendISCSIPortal(pool)) == NULL) return -1; if (virStorageBackendISCSILogin(pool, portal) < 0) { VIR_FREE(portal); return -1; } VIR_FREE(portal); } else { VIR_FREE(session); } return 0; }
static int virStorageBackendLogicalFindLVs(virStoragePoolObjPtr pool, virStorageVolDefPtr vol) { /* * # lvs --separator , --noheadings --units b --unbuffered --nosuffix --options "lv_name,origin,uuid,devices,seg_size,vg_extent_size" VGNAME * RootLV,,06UgP5-2rhb-w3Bo-3mdR-WeoL-pytO-SAa2ky,/dev/hda2(0),5234491392,33554432 * SwapLV,,oHviCK-8Ik0-paqS-V20c-nkhY-Bm1e-zgzU0M,/dev/hda2(156),1040187392,33554432 * Test2,,3pg3he-mQsA-5Sui-h0i6-HNmc-Cz7W-QSndcR,/dev/hda2(219),1073741824,33554432 * Test3,,UB5hFw-kmlm-LSoX-EI1t-ioVd-h7GL-M0W8Ht,/dev/hda2(251),2181038080,33554432 * Test3,Test2,UB5hFw-kmlm-LSoX-EI1t-ioVd-h7GL-M0W8Ht,/dev/hda2(187),1040187392,33554432 * * Pull out name, origin, & uuid, device, device extent start #, segment size, extent size. * * NB can be multiple rows per volume if they have many extents * * NB lvs from some distros (e.g. SLES10 SP2) outputs trailing "," on each line * * NB Encrypted logical volumes can print ':' in their name, so it is * not a suitable separator (rhbz 470693). */ const char *regexes[] = { "^\\s*(\\S+),(\\S*),(\\S+),(\\S+)\\((\\S+)\\),(\\S+),([0-9]+),?\\s*$" }; int vars[] = { 7 }; const char *prog[] = { LVS, "--separator", ",", "--noheadings", "--units", "b", "--unbuffered", "--nosuffix", "--options", "lv_name,origin,uuid,devices,seg_size,vg_extent_size", pool->def->source.name, NULL }; int exitstatus; if (virStorageBackendRunProgRegex(pool, prog, 1, regexes, vars, virStorageBackendLogicalMakeVol, vol, &exitstatus) < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("lvs command failed")); return -1; } if (exitstatus != 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("lvs command failed with exitstatus %d"), exitstatus); return -1; } return 0; }
static int volStorageBackendRBDRefreshVolInfo(virStorageVolDefPtr vol, virStoragePoolObjPtr pool, virStorageBackendRBDStatePtr ptr) { int ret = -1; rbd_image_t image; if (rbd_open(ptr.ioctx, vol->name, &image, NULL) < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("failed to open the RBD image '%s'"), vol->name); return ret; } rbd_image_info_t info; if (rbd_stat(image, &info, sizeof(info)) < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("failed to stat the RBD image")); goto cleanup; } VIR_DEBUG("Refreshed RBD image %s/%s (size: %llu obj_size: %llu num_objs: %llu)", pool->def->source.name, vol->name, (unsigned long long)info.size, (unsigned long long)info.obj_size, (unsigned long long)info.num_objs); vol->capacity = info.size; vol->allocation = info.obj_size * info.num_objs; vol->type = VIR_STORAGE_VOL_NETWORK; VIR_FREE(vol->target.path); if (virAsprintf(&vol->target.path, "rbd:%s/%s", pool->def->source.name, vol->name) == -1) { virReportOOMError(); goto cleanup; } VIR_FREE(vol->key); if (virAsprintf(&vol->key, "%s/%s", pool->def->source.name, vol->name) == -1) { virReportOOMError(); goto cleanup; } ret = 0; cleanup: rbd_close(image); return ret; }
static int virStorageBackendISCSIConnectionIQN(virStoragePoolObjPtr pool, const char *portal, const char *action) { int ret = -1; char *ifacename = NULL; switch (virStorageBackendIQNFound(pool, &ifacename)) { case IQN_FOUND: VIR_DEBUG("ifacename: '%s'", ifacename); break; case IQN_MISSING: if (virStorageBackendCreateIfaceIQN(pool, &ifacename) != 0) { goto out; } break; case IQN_ERROR: default: goto out; } const char *const sendtargets[] = { ISCSIADM, "--mode", "discovery", "--type", "sendtargets", "--portal", portal, NULL }; if (virRun(sendtargets, NULL) < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("Failed to run %s to get target list"), sendtargets[0]); goto out; } const char *const cmdargv[] = { ISCSIADM, "--mode", "node", "--portal", portal, "--targetname", pool->def->source.devices[0].path, "--interface", ifacename, action, NULL }; if (virRun(cmdargv, NULL) < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("Failed to run command '%s' with action '%s'"), cmdargv[0], action); goto out; } ret = 0; out: VIR_FREE(ifacename); return ret; }
static int virStorageBackendRBDCreateVol(virConnectPtr conn, virStoragePoolObjPtr pool, virStorageVolDefPtr vol) { virStorageBackendRBDStatePtr ptr; ptr.cluster = NULL; ptr.ioctx = NULL; int order = 0; int ret = -1; VIR_DEBUG("Creating RBD image %s/%s with size %llu", pool->def->source.name, vol->name, vol->capacity); if (virStorageBackendRBDOpenRADOSConn(&ptr, conn, pool) < 0) { goto cleanup; } if (rados_ioctx_create(ptr.cluster, pool->def->source.name,&ptr.ioctx) < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("failed to create the RBD IoCTX. Does the pool '%s' exist?"), pool->def->source.name); goto cleanup; } if (vol->target.encryption != NULL) { virStorageReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("storage pool does not support encrypted volumes")); goto cleanup; } if (rbd_create(ptr.ioctx, vol->name, vol->capacity, &order) < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("failed to create volume '%s/%s'"), pool->def->source.name, vol->name); goto cleanup; } if (volStorageBackendRBDRefreshVolInfo(vol, pool, ptr) < 0) { goto cleanup; } ret = 0; cleanup: virStorageBackendRBDCloseRADOSConn(ptr); return ret; }
static int virStorageBackendRBDResizeVol(virConnectPtr conn ATTRIBUTE_UNUSED, virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, virStorageVolDefPtr vol, unsigned long long capacity, unsigned int flags) { virStorageBackendRBDStatePtr ptr; ptr.cluster = NULL; ptr.ioctx = NULL; rbd_image_t image = NULL; int ret = -1; virCheckFlags(0, -1); if (virStorageBackendRBDOpenRADOSConn(&ptr, conn, pool) < 0) { goto cleanup; } if (rados_ioctx_create(ptr.cluster, pool->def->source.name, &ptr.ioctx) < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("failed to create the RBD IoCTX. Does the pool '%s' exist?"), pool->def->source.name); goto cleanup; } if (rbd_open(ptr.ioctx, vol->name, &image, NULL) < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("failed to open the RBD image '%s'"), vol->name); goto cleanup; } if (rbd_resize(image, capacity) < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("failed to resize the RBD image '%s'"), vol->name); goto cleanup; } ret = 0; cleanup: if (image != NULL) rbd_close(image); virStorageBackendRBDCloseRADOSConn(ptr); return ret; }
static int createFileDir(virConnectPtr conn ATTRIBUTE_UNUSED, virStoragePoolObjPtr pool, virStorageVolDefPtr vol, virStorageVolDefPtr inputvol, unsigned int flags ATTRIBUTE_UNUSED) { int err; if (inputvol) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot copy from volume to a directory volume")); return -1; } uid_t uid = (vol->target.perms.uid == -1) ? getuid() : vol->target.perms.uid; gid_t gid = (vol->target.perms.gid == -1) ? getgid() : vol->target.perms.gid; if ((err = virDirCreate(vol->target.path, vol->target.perms.mode, uid, gid, VIR_DIR_CREATE_FORCE_PERMS | (pool->def->type == VIR_STORAGE_POOL_NETFS ? VIR_DIR_CREATE_AS_UID : 0))) != 0) { virReportSystemError(err, _("cannot create path '%s'"), vol->target.path); return -1; } return 0; }
static int virStorageBackendRBDRefreshVol(virConnectPtr conn, virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, virStorageVolDefPtr vol) { virStorageBackendRBDStatePtr ptr; ptr.cluster = NULL; ptr.ioctx = NULL; int ret = -1; if (virStorageBackendRBDOpenRADOSConn(&ptr, conn, pool) < 0) { goto cleanup; } if (rados_ioctx_create(ptr.cluster, pool->def->source.name, &ptr.ioctx) < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("failed to create the RBD IoCTX. Does the pool '%s' exist?"), pool->def->source.name); goto cleanup; } if (volStorageBackendRBDRefreshVolInfo(vol, pool, ptr) < 0) { goto cleanup; } ret = 0; cleanup: virStorageBackendRBDCloseRADOSConn(ptr); return ret; }
static int virStorageBackendFileSystemNetFindPoolSourcesFunc(virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, char **const groups, void *data) { virNetfsDiscoverState *state = data; const char *name, *path; virStoragePoolSource *src = NULL; int ret = -1; path = groups[0]; name = strrchr(path, '/'); if (name == NULL) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("invalid netfs path (no /): %s"), path); goto cleanup; } name += 1; if (*name == '\0') { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("invalid netfs path (ends in /): %s"), path); goto cleanup; } if (!(src = virStoragePoolSourceListNewSource(&state->list))) goto cleanup; if (src->nhost != 1) { virStorageReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Expected exactly 1 host for the storage pool")); goto cleanup; } if (!(src->hosts[0].name = strdup(state->host)) || !(src->dir = strdup(path))) { virReportOOMError(); goto cleanup; } src->format = VIR_STORAGE_POOL_NETFS_NFS; src = NULL; ret = 0; cleanup: virStoragePoolSourceFree(src); return ret; }
static int virStorageBackendISCSIStartPool(virConnectPtr conn ATTRIBUTE_UNUSED, virStoragePoolObjPtr pool) { char *portal = NULL; char *session = NULL; int ret = -1; const char *loginargv[] = { "--login", NULL }; if (pool->def->source.host.name == NULL) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing source host")); return -1; } if (pool->def->source.ndevice != 1 || pool->def->source.devices[0].path == NULL) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing source device")); return -1; } if ((session = virStorageBackendISCSISession(pool, 1)) == NULL) { if ((portal = virStorageBackendISCSIPortal(&pool->def->source)) == NULL) goto cleanup; /* * iscsiadm doesn't let you login to a target, unless you've * first issued a 'sendtargets' command to the portal :-( */ if (virStorageBackendISCSIScanTargets(portal, pool->def->source.initiator.iqn, NULL, NULL) < 0) goto cleanup; if (virStorageBackendISCSIConnection(portal, pool->def->source.initiator.iqn, pool->def->source.devices[0].path, loginargv) < 0) goto cleanup; } ret = 0; cleanup: VIR_FREE(session); return ret; }
/** * @conn connection to report errors against * @pool storage pool to unmount * * Ensure that a FS storage pool is not mounted on its target location. * If already unmounted, this is a no-op * * Returns 0 if successfully unmounted, -1 on error */ static int virStorageBackendFileSystemUnmount(virStoragePoolObjPtr pool) { const char *mntargv[3]; int ret; if (pool->def->type == VIR_STORAGE_POOL_NETFS) { if (pool->def->source.nhost != 1) { virStorageReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Expected exactly 1 host for the storage pool")); return -1; } if (pool->def->source.hosts[0].name == NULL) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing source host")); return -1; } if (pool->def->source.dir == NULL) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing source dir")); return -1; } } else { if (pool->def->source.ndevice != 1) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing source device")); return -1; } } /* Short-circuit if already unmounted */ if ((ret = virStorageBackendFileSystemIsMounted(pool)) != 1) { if (ret < 0) return -1; else return 0; } mntargv[0] = UMOUNT; mntargv[1] = pool->def->target.path; mntargv[2] = NULL; if (virRun(mntargv, NULL) < 0) { return -1; } return 0; }
static int virStorageBackendISCSITargetIP(const char *hostname, char *ipaddr, size_t ipaddrlen) { struct addrinfo hints; struct addrinfo *result = NULL; int ret; memset(&hints, 0, sizeof hints); hints.ai_flags = AI_ADDRCONFIG; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; ret = getaddrinfo(hostname, NULL, &hints, &result); if (ret != 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("host lookup failed %s"), gai_strerror(ret)); return -1; } if (result == NULL) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("no IP address for target %s"), hostname); return -1; } if (getnameinfo(result->ai_addr, result->ai_addrlen, ipaddr, ipaddrlen, NULL, 0, NI_NUMERICHOST) < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("cannot format ip addr for %s"), hostname); freeaddrinfo(result); return -1; } freeaddrinfo(result); return 0; }
static int virStorageBackendRBDDeleteVol(virConnectPtr conn, virStoragePoolObjPtr pool, virStorageVolDefPtr vol, unsigned int flags) { int ret = -1; virStorageBackendRBDStatePtr ptr; ptr.cluster = NULL; ptr.ioctx = NULL; VIR_DEBUG("Removing RBD image %s/%s", pool->def->source.name, vol->name); if (flags & VIR_STORAGE_VOL_DELETE_ZEROED) { VIR_WARN("%s", _("This storage backend does not supported zeroed removal of volumes")); } if (virStorageBackendRBDOpenRADOSConn(&ptr, conn, pool) < 0) { goto cleanup; } if (rados_ioctx_create(ptr.cluster, pool->def->source.name, &ptr.ioctx) < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("failed to create the RBD IoCTX. Does the pool '%s' exist?"), pool->def->source.name); goto cleanup; } if (rbd_remove(ptr.ioctx, vol->name) < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("failed to remove volume '%s/%s'"), pool->def->source.name, vol->name); goto cleanup; } ret = 0; cleanup: virStorageBackendRBDCloseRADOSConn(ptr); return ret; }
static int _virStorageBackendFileSystemVolBuild(virConnectPtr conn, virStoragePoolObjPtr pool, virStorageVolDefPtr vol, virStorageVolDefPtr inputvol) { virStorageBackendBuildVolFrom create_func; int tool_type; if (inputvol) { if (vol->target.encryption != NULL) { virStorageReportError(VIR_ERR_NO_SUPPORT, "%s", _("storage pool does not support " "building encrypted volumes from " "other volumes")); return -1; } create_func = virStorageBackendGetBuildVolFromFunction(vol, inputvol); if (!create_func) return -1; } else if (vol->target.format == VIR_STORAGE_FILE_RAW) { create_func = virStorageBackendCreateRaw; } else if (vol->target.format == VIR_STORAGE_FILE_DIR) { create_func = createFileDir; } else if ((tool_type = virStorageBackendFindFSImageTool(NULL)) != -1) { create_func = virStorageBackendFSImageToolTypeToFunc(tool_type); if (!create_func) return -1; } else { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("creation of non-raw images " "is not supported without qemu-img")); return -1; } if (create_func(conn, pool, vol, inputvol, 0) < 0) return -1; return 0; }
static int virStorageBackendCreateVols(virStoragePoolObjPtr pool, struct dm_names *names) { int retval = -1, is_mpath = 0; char *map_device = NULL; uint32_t minor = -1; uint32_t next; do { is_mpath = virStorageBackendIsMultipath(names->name); if (is_mpath < 0) { goto out; } if (is_mpath == 1) { if (virAsprintf(&map_device, "mapper/%s", names->name) < 0) { virReportOOMError(); goto out; } if (virStorageBackendGetMinorNumber(names->name, &minor) < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("Failed to get %s minor number"), names->name); goto out; } if (virStorageBackendMpathNewVol(pool, minor, map_device) < 0) { goto out; } VIR_FREE(map_device); } /* Given the way libdevmapper returns its data, I don't see * any way to avoid this series of casts. */ next = names->next; names = (struct dm_names *)(((char *)names) + next); } while (next); retval = 0; out: return retval; }
static char * virStorageBackendISCSISession(virStoragePoolObjPtr pool, int probe) { /* * # iscsiadm --mode session * tcp: [1] 192.168.122.170:3260,1 demo-tgt-b * tcp: [2] 192.168.122.170:3260,1 demo-tgt-a * * Pull out 2nd and 4th fields */ const char *regexes[] = { "^tcp:\\s+\\[(\\S+)\\]\\s+\\S+\\s+(\\S+)\\s*$" }; int vars[] = { 2, }; const char *const prog[] = { ISCSIADM, "--mode", "session", NULL }; char *session = NULL; /* Note that we ignore the exitstatus. Older versions of iscsiadm tools * returned an exit status of > 0, even if they succeeded. We will just * rely on whether session got filled in properly. */ if (virStorageBackendRunProgRegex(pool, prog, 1, regexes, vars, virStorageBackendISCSIExtractSession, &session, NULL) < 0) return NULL; if (session == NULL && !probe) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot find session")); return NULL; } return session; }
virStorageBackendProbeTarget(virStorageVolTargetPtr target, char **backingStore, int *backingStoreFormat, unsigned long long *allocation, unsigned long long *capacity, virStorageEncryptionPtr *encryption) { int fd = -1; int ret = -1; virStorageFileMetadata *meta; if (VIR_ALLOC(meta) < 0) { virReportOOMError(); return ret; } *backingStore = NULL; *backingStoreFormat = VIR_STORAGE_FILE_AUTO; if (encryption) *encryption = NULL; if ((ret = virStorageBackendVolOpenCheckMode(target->path, VIR_STORAGE_VOL_FS_REFRESH_FLAGS)) < 0) goto error; /* Take care to propagate ret, it is not always -1 */ fd = ret; if ((ret = virStorageBackendUpdateVolTargetInfoFD(target, fd, allocation, capacity)) < 0) { goto error; } if ((target->format = virStorageFileProbeFormatFromFD(target->path, fd)) < 0) { ret = -1; goto error; } if (virStorageFileGetMetadataFromFD(target->path, fd, target->format, meta) < 0) { ret = -1; goto error; } VIR_FORCE_CLOSE(fd); if (meta->backingStore) { *backingStore = meta->backingStore; meta->backingStore = NULL; if (meta->backingStoreFormat == VIR_STORAGE_FILE_AUTO) { if ((ret = virStorageFileProbeFormat(*backingStore)) < 0) { /* If the backing file is currently unavailable, only log an error, * but continue. Returning -1 here would disable the whole storage * pool, making it unavailable for even maintenance. */ virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("cannot probe backing volume format: %s"), *backingStore); ret = -3; } else { *backingStoreFormat = ret; ret = 0; } } else { *backingStoreFormat = meta->backingStoreFormat; ret = 0; } } else { ret = 0; } if (capacity && meta->capacity) *capacity = meta->capacity; if (encryption != NULL && meta->encrypted) { if (VIR_ALLOC(*encryption) < 0) { virReportOOMError(); goto cleanup; } switch (target->format) { case VIR_STORAGE_FILE_QCOW: case VIR_STORAGE_FILE_QCOW2: (*encryption)->format = VIR_STORAGE_ENCRYPTION_FORMAT_QCOW; break; default: break; } /* XXX ideally we'd fill in secret UUID here * but we cannot guarantee 'conn' is non-NULL * at this point in time :-( So we only fill * in secrets when someone first queries a vol */ } virStorageFileFreeMetadata(meta); return ret; error: VIR_FORCE_CLOSE(fd); cleanup: virStorageFileFreeMetadata(meta); return ret; }
/** * @conn connection to report errors against * @pool storage pool to mount * * Ensure that a FS storage pool is mounted on its target location. * If already mounted, this is a no-op * * Returns 0 if successfully mounted, -1 on error */ static int virStorageBackendFileSystemMount(virStoragePoolObjPtr pool) { char *src; const char **mntargv; /* 'mount -t auto' doesn't seem to auto determine nfs (or cifs), * while plain 'mount' does. We have to craft separate argvs to * accommodate this */ int netauto = (pool->def->type == VIR_STORAGE_POOL_NETFS && pool->def->source.format == VIR_STORAGE_POOL_NETFS_AUTO); int glusterfs = (pool->def->type == VIR_STORAGE_POOL_NETFS && pool->def->source.format == VIR_STORAGE_POOL_NETFS_GLUSTERFS); int source_index; const char *netfs_auto_argv[] = { MOUNT, NULL, /* source path */ pool->def->target.path, NULL, }; const char *fs_argv[] = { MOUNT, "-t", pool->def->type == VIR_STORAGE_POOL_FS ? virStoragePoolFormatFileSystemTypeToString(pool->def->source.format) : virStoragePoolFormatFileSystemNetTypeToString(pool->def->source.format), NULL, /* Fill in shortly - careful not to add extra fields before this */ pool->def->target.path, NULL, }; const char *glusterfs_argv[] = { MOUNT, "-t", pool->def->type == VIR_STORAGE_POOL_FS ? virStoragePoolFormatFileSystemTypeToString(pool->def->source.format) : virStoragePoolFormatFileSystemNetTypeToString(pool->def->source.format), NULL, "-o", "direct-io-mode=1", pool->def->target.path, NULL, }; if (netauto) { mntargv = netfs_auto_argv; source_index = 1; } else if (glusterfs) { mntargv = glusterfs_argv; source_index = 3; } else { mntargv = fs_argv; source_index = 3; } int ret; if (pool->def->type == VIR_STORAGE_POOL_NETFS) { if (pool->def->source.host.name == NULL) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing source host")); return -1; } if (pool->def->source.dir == NULL) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing source path")); return -1; } } else { if (pool->def->source.ndevice != 1) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing source device")); return -1; } } /* Short-circuit if already mounted */ if ((ret = virStorageBackendFileSystemIsMounted(pool)) != 0) { if (ret < 0) return -1; else return 0; } if (pool->def->type == VIR_STORAGE_POOL_NETFS) { if (virAsprintf(&src, "%s:%s", pool->def->source.host.name, pool->def->source.dir) == -1) { virReportOOMError(); return -1; } } else { if ((src = strdup(pool->def->source.devices[0].path)) == NULL) { virReportOOMError(); return -1; } } mntargv[source_index] = src; if (virRun(mntargv, NULL) < 0) { VIR_FREE(src); return -1; } VIR_FREE(src); return 0; }
static virStoragePoolProbeResult virStorageBackendFileSystemProbe(const char *device, const char *format) { virStoragePoolProbeResult ret = FILESYSTEM_PROBE_ERROR; blkid_probe probe = NULL; const char *fstype = NULL; char *names[2], *libblkid_format = NULL; VIR_DEBUG("Probing for existing filesystem of type %s on device %s", format, device); if (blkid_known_fstype(format) == 0) { virStorageReportError(VIR_ERR_STORAGE_PROBE_FAILED, _("Not capable of probing for " "filesystem of type %s"), format); goto error; } probe = blkid_new_probe_from_filename(device); if (probe == NULL) { virStorageReportError(VIR_ERR_STORAGE_PROBE_FAILED, _("Failed to create filesystem probe " "for device %s"), device); goto error; } if ((libblkid_format = strdup(format)) == NULL) { virReportOOMError(); goto error; } names[0] = libblkid_format; names[1] = NULL; blkid_probe_filter_superblocks_type(probe, BLKID_FLTR_ONLYIN, names); if (blkid_do_probe(probe) != 0) { VIR_INFO("No filesystem of type '%s' found on device '%s'", format, device); ret = FILESYSTEM_PROBE_NOT_FOUND; } else if (blkid_probe_lookup_value(probe, "TYPE", &fstype, NULL) == 0) { virStorageReportError(VIR_ERR_STORAGE_POOL_BUILT, _("Existing filesystem of type '%s' found on " "device '%s'"), fstype, device); ret = FILESYSTEM_PROBE_FOUND; } if (blkid_do_probe(probe) != 1) { virStorageReportError(VIR_ERR_STORAGE_PROBE_FAILED, _("Found additional probes to run, " "filesystem probing may be incorrect")); ret = FILESYSTEM_PROBE_ERROR; } error: VIR_FREE(libblkid_format); if (probe != NULL) { blkid_free_probe(probe); } return ret; }
static char * virStorageBackendISCSIFindPoolSources(virConnectPtr conn ATTRIBUTE_UNUSED, const char *srcSpec, unsigned int flags) { virStoragePoolSourcePtr source = NULL; size_t ntargets = 0; char **targets = NULL; char *ret = NULL; int i; virStoragePoolSourceList list = { .type = VIR_STORAGE_POOL_ISCSI, .nsources = 0, .sources = NULL }; char *portal = NULL; virCheckFlags(0, NULL); if (!(source = virStoragePoolDefParseSourceString(srcSpec, list.type))) return NULL; if (!(portal = virStorageBackendISCSIPortal(source))) goto cleanup; if (virStorageBackendISCSIScanTargets(portal, source->initiator.iqn, &ntargets, &targets) < 0) goto cleanup; if (VIR_ALLOC_N(list.sources, ntargets) < 0) { virReportOOMError(); goto cleanup; } for (i = 0 ; i < ntargets ; i++) { if (VIR_ALLOC_N(list.sources[i].devices, 1) < 0) { virReportOOMError(); goto cleanup; } list.sources[i].host = source->host; list.sources[i].initiator = source->initiator; list.sources[i].ndevice = 1; list.sources[i].devices[0].path = targets[i]; list.nsources++; } if (!(ret = virStoragePoolSourceListFormat(&list))) { virReportOOMError(); goto cleanup; } cleanup: if (list.sources) { for (i = 0 ; i < ntargets ; i++) VIR_FREE(list.sources[i].devices); VIR_FREE(list.sources); } for (i = 0 ; i < ntargets ; i++) VIR_FREE(targets[i]); VIR_FREE(targets); VIR_FREE(portal); virStoragePoolSourceFree(source); return ret; } static int virStorageBackendISCSICheckPool(virConnectPtr conn ATTRIBUTE_UNUSED, virStoragePoolObjPtr pool, bool *isActive) { char *session = NULL; int ret = -1; *isActive = false; if (pool->def->source.host.name == NULL) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing source host")); return -1; } if (pool->def->source.ndevice != 1 || pool->def->source.devices[0].path == NULL) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing source device")); return -1; } if ((session = virStorageBackendISCSISession(pool, 1)) != NULL) { *isActive = true; VIR_FREE(session); } ret = 0; return ret; }
static int virStorageBackendRBDRefreshPool(virConnectPtr conn ATTRIBUTE_UNUSED, virStoragePoolObjPtr pool) { size_t max_size = 1024; int ret = -1; int len = -1; int i; char *name, *names = NULL; virStorageBackendRBDStatePtr ptr; ptr.cluster = NULL; ptr.ioctx = NULL; if (virStorageBackendRBDOpenRADOSConn(&ptr, conn, pool) < 0) { goto cleanup; } if (rados_ioctx_create(ptr.cluster, pool->def->source.name, &ptr.ioctx) < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("failed to create the RBD IoCTX. Does the pool '%s' exist?"), pool->def->source.name); goto cleanup; } struct rados_cluster_stat_t stat; if (rados_cluster_stat(ptr.cluster, &stat) < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("failed to stat the RADOS cluster")); goto cleanup; } struct rados_pool_stat_t poolstat; if (rados_ioctx_pool_stat(ptr.ioctx, &poolstat) < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("failed to stat the RADOS pool '%s'"), pool->def->source.name); goto cleanup; } pool->def->capacity = stat.kb * 1024; pool->def->available = stat.kb_avail * 1024; pool->def->allocation = poolstat.num_bytes; VIR_DEBUG("Utilization of RBD pool %s: (kb: %llu kb_avail: %llu num_bytes: %llu)", pool->def->source.name, (unsigned long long)stat.kb, (unsigned long long)stat.kb_avail, (unsigned long long)poolstat.num_bytes); while (true) { if (VIR_ALLOC_N(names, max_size) < 0) goto out_of_memory; len = rbd_list(ptr.ioctx, names, &max_size); if (len >= 0) break; if (len != -ERANGE) { VIR_WARN("%s", _("A problem occured while listing RBD images")); goto cleanup; } } for (i = 0, name = names; name < names + max_size; i++) { if (VIR_REALLOC_N(pool->volumes.objs, pool->volumes.count + 1) < 0) { virStoragePoolObjClearVols(pool); goto out_of_memory; } virStorageVolDefPtr vol; if (VIR_ALLOC(vol) < 0) goto out_of_memory; vol->name = strdup(name); if (vol->name == NULL) goto out_of_memory; if (STREQ(vol->name, "")) break; name += strlen(name) + 1; if (volStorageBackendRBDRefreshVolInfo(vol, pool, ptr) < 0) goto cleanup; pool->volumes.objs[pool->volumes.count++] = vol; } VIR_DEBUG("Found %d images in RBD pool %s", pool->volumes.count, pool->def->source.name); ret = 0; cleanup: VIR_FREE(names); virStorageBackendRBDCloseRADOSConn(ptr); return ret; out_of_memory: virReportOOMError(); goto cleanup; }
static int virStorageBackendRBDOpenRADOSConn(virStorageBackendRBDStatePtr *ptr, virConnectPtr conn, virStoragePoolObjPtr pool) { int ret = -1; unsigned char *secret_value = NULL; size_t secret_value_size; char *rados_key = NULL; virBuffer mon_host = VIR_BUFFER_INITIALIZER; virSecretPtr secret = NULL; char secretUuid[VIR_UUID_STRING_BUFLEN]; int i; char *mon_buff = NULL; VIR_DEBUG("Found Cephx username: %s", pool->def->source.auth.cephx.username); if (pool->def->source.auth.cephx.username != NULL) { VIR_DEBUG("Using cephx authorization"); if (rados_create(&ptr->cluster, pool->def->source.auth.cephx.username) < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("failed to initialize RADOS")); goto cleanup; } if (pool->def->source.auth.cephx.secret.uuid != NULL) { virUUIDFormat(pool->def->source.auth.cephx.secret.uuid, secretUuid); VIR_DEBUG("Looking up secret by UUID: %s", secretUuid); secret = virSecretLookupByUUIDString(conn, secretUuid); } if (pool->def->source.auth.cephx.secret.usage != NULL) { VIR_DEBUG("Looking up secret by usage: %s", pool->def->source.auth.cephx.secret.usage); secret = virSecretLookupByUsage(conn, VIR_SECRET_USAGE_TYPE_CEPH, pool->def->source.auth.cephx.secret.usage); } if (secret == NULL) { virStorageReportError(VIR_ERR_NO_SECRET, _("failed to find the secret")); goto cleanup; } secret_value = virSecretGetValue(secret, &secret_value_size, 0); base64_encode_alloc((char *)secret_value, secret_value_size, &rados_key); memset(secret_value, 0, secret_value_size); if (rados_key == NULL) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("failed to decode the RADOS key")); goto cleanup; } VIR_DEBUG("Found cephx key: %s", rados_key); if (rados_conf_set(ptr->cluster, "key", rados_key) < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("failed to set RADOS option: %s"), "rados_key"); goto cleanup; } memset(rados_key, 0, strlen(rados_key)); if (rados_conf_set(ptr->cluster, "auth_supported", "cephx") < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("failed to set RADOS option: %s"), "auth_supported"); goto cleanup; } } else { VIR_DEBUG("Not using cephx authorization"); if (rados_create(&ptr->cluster, NULL) < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("failed to create the RADOS cluster")); goto cleanup; } if (rados_conf_set(ptr->cluster, "auth_supported", "none") < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("failed to set RADOS option: %s"), "auth_supported"); goto cleanup; } } VIR_DEBUG("Found %zu RADOS cluster monitors in the pool configuration", pool->def->source.nhost); for (i = 0; i < pool->def->source.nhost; i++) { if (pool->def->source.hosts[i].name != NULL && !pool->def->source.hosts[i].port) { virBufferAsprintf(&mon_host, "%s:6789,", pool->def->source.hosts[i].name); } else if (pool->def->source.hosts[i].name != NULL && pool->def->source.hosts[i].port) { virBufferAsprintf(&mon_host, "%s:%d,", pool->def->source.hosts[i].name, pool->def->source.hosts[i].port); } else { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("received malformed monitor, check the XML definition")); } } if (virBufferError(&mon_host)) { virReportOOMError(); goto cleanup; } mon_buff = virBufferContentAndReset(&mon_host); VIR_DEBUG("RADOS mon_host has been set to: %s", mon_buff); if (rados_conf_set(ptr->cluster, "mon_host", mon_buff) < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("failed to set RADOS option: %s"), "mon_host"); goto cleanup; } ptr->starttime = time(0); if (rados_connect(ptr->cluster) < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("failed to connect to the RADOS monitor on: %s"), mon_buff); goto cleanup; } ret = 0; cleanup: VIR_FREE(secret_value); VIR_FREE(rados_key); virSecretFree(secret); virBufferFreeAndReset(&mon_host); VIR_FREE(mon_buff); return ret; }
static int virStorageBackendCreateIfaceIQN(const char *initiatoriqn, char **ifacename) { int ret = -1, exitstatus = -1; char temp_ifacename[32]; const char *const cmdargv1[] = { ISCSIADM, "--mode", "iface", "--interface", temp_ifacename, "--op", "new", NULL }; const char *const cmdargv2[] = { ISCSIADM, "--mode", "iface", "--interface", temp_ifacename, "--op", "update", "--name", "iface.initiatorname", "--value", initiatoriqn, NULL }; if (virRandomInitialize(time(NULL) ^ getpid()) == -1) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Failed to initialize random generator " "when creating iscsi interface")); goto out; } snprintf(temp_ifacename, sizeof(temp_ifacename), "libvirt-iface-%08x", virRandom(1024 * 1024 * 1024)); VIR_DEBUG("Attempting to create interface '%s' with IQN '%s'", &temp_ifacename[0], initiatoriqn); /* Note that we ignore the exitstatus. Older versions of iscsiadm * tools returned an exit status of > 0, even if they succeeded. * We will just rely on whether the interface got created * properly. */ if (virRun(cmdargv1, &exitstatus) < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("Failed to run command '%s' to create new iscsi interface"), cmdargv1[0]); goto out; } /* Note that we ignore the exitstatus. Older versions of iscsiadm tools * returned an exit status of > 0, even if they succeeded. We will just * rely on whether iface file got updated properly. */ if (virRun(cmdargv2, &exitstatus) < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("Failed to run command '%s' to update iscsi interface with IQN '%s'"), cmdargv2[0], initiatoriqn); goto out; } /* Check again to make sure the interface was created. */ if (virStorageBackendIQNFound(initiatoriqn, ifacename) != IQN_FOUND) { VIR_DEBUG("Failed to find interface '%s' with IQN '%s' " "after attempting to create it", &temp_ifacename[0], initiatoriqn); goto out; } else { VIR_DEBUG("Interface '%s' with IQN '%s' was created successfully", *ifacename, initiatoriqn); } ret = 0; out: if (ret != 0) VIR_FREE(*ifacename); return ret; }
static int virStorageBackendIQNFound(const char *initiatoriqn, char **ifacename) { int ret = IQN_MISSING, fd = -1; char ebuf[64]; FILE *fp = NULL; char *line = NULL, *newline = NULL, *iqn = NULL, *token = NULL; virCommandPtr cmd = virCommandNewArgList(ISCSIADM, "--mode", "iface", NULL); if (VIR_ALLOC_N(line, LINE_SIZE) != 0) { ret = IQN_ERROR; virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("Could not allocate memory for output of '%s'"), ISCSIADM); goto out; } memset(line, 0, LINE_SIZE); virCommandSetOutputFD(cmd, &fd); if (virCommandRunAsync(cmd, NULL) < 0) { ret = IQN_ERROR; goto out; } if ((fp = VIR_FDOPEN(fd, "r")) == NULL) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("Failed to open stream for file descriptor " "when reading output from '%s': '%s'"), ISCSIADM, virStrerror(errno, ebuf, sizeof ebuf)); ret = IQN_ERROR; goto out; } while (fgets(line, LINE_SIZE, fp) != NULL) { newline = strrchr(line, '\n'); if (newline == NULL) { ret = IQN_ERROR; virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("Unexpected line > %d characters " "when parsing output of '%s'"), LINE_SIZE, ISCSIADM); goto out; } *newline = '\0'; iqn = strrchr(line, ','); if (iqn == NULL) { continue; } iqn++; if (STREQ(iqn, initiatoriqn)) { token = strchr(line, ' '); if (!token) { ret = IQN_ERROR; virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("Missing space when parsing output " "of '%s'"), ISCSIADM); goto out; } *ifacename = strndup(line, token - line); if (*ifacename == NULL) { ret = IQN_ERROR; virReportOOMError(); goto out; } VIR_DEBUG("Found interface '%s' with IQN '%s'", *ifacename, iqn); ret = IQN_FOUND; break; } } if (virCommandWait(cmd, NULL) < 0) ret = IQN_ERROR; out: if (ret == IQN_MISSING) { VIR_DEBUG("Could not find interface with IQN '%s'", iqn); } VIR_FREE(line); VIR_FORCE_FCLOSE(fp); VIR_FORCE_CLOSE(fd); virCommandFree(cmd); return ret; }
static char * virStorageBackendFileSystemNetFindPoolSources(virConnectPtr conn ATTRIBUTE_UNUSED, const char *srcSpec, unsigned int flags) { /* * # showmount --no-headers -e HOSTNAME * /tmp * * /A dir demo1.foo.bar,demo2.foo.bar * * Extract directory name (including possible interior spaces ...). */ const char *regexes[] = { "^(/.*\\S) +\\S+$" }; int vars[] = { 1 }; virNetfsDiscoverState state = { .host = NULL, .list = { .type = VIR_STORAGE_POOL_NETFS, .nsources = 0, .sources = NULL } }; const char *prog[] = { SHOWMOUNT, "--no-headers", "--exports", NULL, NULL }; virStoragePoolSourcePtr source = NULL; char *retval = NULL; unsigned int i; virCheckFlags(0, NULL); source = virStoragePoolDefParseSourceString(srcSpec, VIR_STORAGE_POOL_NETFS); if (!source) goto cleanup; if (source->nhost != 1) { virStorageReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Expected exactly 1 host for the storage pool")); goto cleanup; } state.host = source->hosts[0].name; prog[3] = source->hosts[0].name; if (virStorageBackendRunProgRegex(NULL, prog, 1, regexes, vars, virStorageBackendFileSystemNetFindPoolSourcesFunc, &state, NULL) < 0) goto cleanup; retval = virStoragePoolSourceListFormat(&state.list); if (retval == NULL) { virReportOOMError(); goto cleanup; } cleanup: for (i = 0; i < state.list.nsources; i++) virStoragePoolSourceClear(&state.list.sources[i]); VIR_FREE(state.list.sources); virStoragePoolSourceFree(source); return retval; }
static int virStorageBackendIQNFound(virStoragePoolObjPtr pool, char **ifacename) { int ret = IQN_MISSING, fd = -1; char ebuf[64]; FILE *fp = NULL; pid_t child = 0; char *line = NULL, *newline = NULL, *iqn = NULL, *token = NULL, *saveptr = NULL; const char *const prog[] = { ISCSIADM, "--mode", "iface", NULL }; if (VIR_ALLOC_N(line, LINE_SIZE) != 0) { ret = IQN_ERROR; virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("Could not allocate memory for output of '%s'"), prog[0]); goto out; } memset(line, 0, LINE_SIZE); if (virExec(prog, NULL, NULL, &child, -1, &fd, NULL, VIR_EXEC_NONE) < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("Failed to run '%s' when looking for existing interface with IQN '%s'"), prog[0], pool->def->source.initiator.iqn); ret = IQN_ERROR; goto out; } if ((fp = fdopen(fd, "r")) == NULL) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("Failed to open stream for file descriptor " "when reading output from '%s': '%s'"), prog[0], virStrerror(errno, ebuf, sizeof ebuf)); ret = IQN_ERROR; goto out; } while (fgets(line, LINE_SIZE, fp) != NULL) { newline = strrchr(line, '\n'); if (newline == NULL) { ret = IQN_ERROR; virStorageReportError(VIR_ERR_INTERNAL_ERROR, _("Unexpected line > %d characters " "when parsing output of '%s'"), LINE_SIZE, prog[0]); goto out; } *newline = '\0'; iqn = strrchr(line, ','); if (iqn == NULL) { continue; } iqn++; if (STREQ(iqn, pool->def->source.initiator.iqn)) { token = strtok_r(line, " ", &saveptr); *ifacename = strdup(token); if (*ifacename == NULL) { ret = IQN_ERROR; virReportOOMError(); goto out; } VIR_DEBUG("Found interface '%s' with IQN '%s'", *ifacename, iqn); ret = IQN_FOUND; break; } } out: if (ret == IQN_MISSING) { VIR_DEBUG("Could not find interface witn IQN '%s'", iqn); } VIR_FREE(line); if (fp != NULL) { fclose(fp); } else { if (fd != -1) { close(fd); } } return ret; }
static int virStorageBackendLogicalMakeVol(virStoragePoolObjPtr pool, char **const groups, void *data) { virStorageVolDefPtr vol = NULL; unsigned long long offset, size, length; /* See if we're only looking for a specific volume */ if (data != NULL) { vol = data; if (STRNEQ(vol->name, groups[0])) return 0; } /* Or filling in more data on an existing volume */ if (vol == NULL) vol = virStorageVolDefFindByName(pool, groups[0]); /* Or a completely new volume */ if (vol == NULL) { if (VIR_ALLOC(vol) < 0) { virReportOOMError(); return -1; } vol->type = VIR_STORAGE_VOL_BLOCK; if ((vol->name = strdup(groups[0])) == NULL) { virReportOOMError(); virStorageVolDefFree(vol); return -1; } if (VIR_REALLOC_N(pool->volumes.objs, pool->volumes.count + 1)) { virReportOOMError(); virStorageVolDefFree(vol); return -1; } pool->volumes.objs[pool->volumes.count++] = vol; } if (vol->target.path == NULL) { if (virAsprintf(&vol->target.path, "%s/%s", pool->def->target.path, vol->name) < 0) { virReportOOMError(); virStorageVolDefFree(vol); return -1; } } if (groups[1] && !STREQ(groups[1], "")) { if (virAsprintf(&vol->backingStore.path, "%s/%s", pool->def->target.path, groups[1]) < 0) { virReportOOMError(); virStorageVolDefFree(vol); return -1; } vol->backingStore.format = VIR_STORAGE_POOL_LOGICAL_LVM2; } if (vol->key == NULL && (vol->key = strdup(groups[2])) == NULL) { virReportOOMError(); return -1; } if (virStorageBackendUpdateVolInfo(vol, 1) < 0) return -1; /* Finally fill in extents information */ if (VIR_REALLOC_N(vol->source.extents, vol->source.nextent + 1) < 0) { virReportOOMError(); return -1; } if ((vol->source.extents[vol->source.nextent].path = strdup(groups[3])) == NULL) { virReportOOMError(); return -1; } if (virStrToLong_ull(groups[4], NULL, 10, &offset) < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("malformed volume extent offset value")); return -1; } if (virStrToLong_ull(groups[5], NULL, 10, &length) < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("malformed volume extent length value")); return -1; } if (virStrToLong_ull(groups[6], NULL, 10, &size) < 0) { virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("malformed volume extent size value")); return -1; } vol->source.extents[vol->source.nextent].start = offset * size; vol->source.extents[vol->source.nextent].end = (offset * size) + length; vol->source.nextent++; return 0; }
/** * @conn connection to report errors against * @pool storage pool to build * * Build a directory or FS based storage pool. * * - If it is a FS based pool, mounts the unlying source device on the pool * * Returns 0 on success, -1 on error */ static int virStorageBackendFileSystemBuild(virConnectPtr conn ATTRIBUTE_UNUSED, virStoragePoolObjPtr pool, unsigned int flags ATTRIBUTE_UNUSED) { int err, ret = -1; char *parent; char *p; if ((parent = strdup(pool->def->target.path)) == NULL) { virReportOOMError(); goto error; } if (!(p = strrchr(parent, '/'))) { virStorageReportError(VIR_ERR_INVALID_ARG, _("path '%s' is not absolute"), pool->def->target.path); goto error; } if (p != parent) { /* assure all directories in the path prior to the final dir * exist, with default uid/gid/mode. */ *p = '\0'; if ((err = virFileMakePath(parent)) != 0) { virReportSystemError(err, _("cannot create path '%s'"), parent); goto error; } } /* Now create the final dir in the path with the uid/gid/mode * requested in the config. If the dir already exists, just set * the perms. */ struct stat st; if ((stat(pool->def->target.path, &st) < 0) || (pool->def->target.perms.uid != -1)) { uid_t uid = (pool->def->target.perms.uid == -1) ? getuid() : pool->def->target.perms.uid; gid_t gid = (pool->def->target.perms.gid == -1) ? getgid() : pool->def->target.perms.gid; if ((err = virDirCreate(pool->def->target.path, pool->def->target.perms.mode, uid, gid, VIR_DIR_CREATE_FORCE_PERMS | VIR_DIR_CREATE_ALLOW_EXIST | (pool->def->type == VIR_STORAGE_POOL_NETFS ? VIR_DIR_CREATE_AS_UID : 0)) != 0)) { virReportSystemError(err, _("cannot create path '%s'"), pool->def->target.path); goto error; } } ret = 0; error: VIR_FREE(parent); return ret; }