#include #include #include #include #include #include #include #include #include #include #include #include "FhgfsOpsSuper.h" #include "FhgfsOpsHelper.h" #include "FhgfsOpsIoctl.h" #include "FhgfsOpsInode.h" #ifdef CONFIG_COMPAT #include #endif #include /* old kernels didn't have the TCGETS ioctl definition (used by isatty() test) */ #ifndef TCGETS #define TCGETS 0x5401 #endif /* define these strings here so the IOCTL interface doesn't depend upon privately known ints */ #define FHGFSOPSIOCTL_NODETYPE_STORAGE "storage" #define FHGFSOPSIOCTL_NODETYPE_META "meta" #define FHGFSOPSIOCTL_NODETYPE_METADATA "metadata" #define FHGFSOPSIOCTL_NODETYPE_MGMT "mgmt" #define FHGFSOPSIOCTL_NODETYPE_MANAGEMENT "management" static long FhgfsOpsIoctl_getCfgFile(struct file *file, void __user *argp); static long FhgfsOpsIoctl_getRuntimeCfgFile(struct file *file, void __user *argp); static long FhgfsOpsIoctl_testIsFhGFS(struct file *file, void __user *argp); static long FhgfsOpsIoctl_createFile(struct file *file, void __user *argp, int version); static long FhgfsOpsIoctl_getMountID(struct file *file, void __user *argp); static long FhgfsOpsIoctl_getStripeInfo(struct file *file, void __user *argp); static long FhgfsOpsIoctl_getStripeTarget(struct file *file, void __user *argp); static long FhgfsOpsIoctl_getStripeTargetV2(struct file *file, void __user *argp); static long FhgfsOpsIoctl_mkfileWithStripeHints(struct file *file, void __user *argp); static long FhgfsOpsIoctl_getInodeID(struct file *file, void __user *argp); static long FhgfsOpsIoctl_getEntryInfo(struct file *file, void __user *argp); static long FhgfsOpsIoctl_pingNode(struct file *file, void __user *argp); static NodeType FhgfsOpsIoctl_strToNodeType(const char* str, size_t len) { if (!strncmp(FHGFSOPSIOCTL_NODETYPE_META, str, len) || !strncmp(FHGFSOPSIOCTL_NODETYPE_METADATA, str, len)) return NODETYPE_Meta; if (!strncmp(FHGFSOPSIOCTL_NODETYPE_STORAGE, str, len)) return NODETYPE_Storage; if (!strncmp(FHGFSOPSIOCTL_NODETYPE_MGMT, str, len) || !strncmp(FHGFSOPSIOCTL_NODETYPE_MANAGEMENT, str, len)) return NODETYPE_Mgmt; return NODETYPE_Invalid; } /** * Execute FhGFS IOCTLs. * * @param file the file the ioctl was opened for * @param cmd the ioctl command supposed to be done * @param arg in and out argument */ long FhgfsOpsIoctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct dentry *dentry = file_dentry(file); struct inode *inode = file_inode(file); App* app = FhgfsOps_getApp(dentry->d_sb); Logger* log = App_getLogger(app); const char* logContext = "FhgfsOps_ioctl"; Logger_logFormatted(log, Log_SPAM, logContext, "Called ioctl cmd: %u", cmd); switch(cmd) { case BEEGFS_IOC_GETVERSION: case BEEGFS_IOC_GETVERSION_OLD: { /* used by userspace filesystems or userspace nfs servers for cases where a (recycled) inode number now refers to a different file (and thus has a different generation number) */ return put_user(inode->i_generation, (int __user *) arg); } break; case BEEGFS_IOC_GET_CFG_FILE: { /* get the path to the client config file*/ return FhgfsOpsIoctl_getCfgFile(file, (void __user *) arg); } break; case BEEGFS_IOC_CREATE_FILE: { return FhgfsOpsIoctl_createFile(file, (void __user *) arg, 1); } break; case BEEGFS_IOC_CREATE_FILE_V2: { return FhgfsOpsIoctl_createFile(file, (void __user *) arg, 2); } break; case BEEGFS_IOC_CREATE_FILE_V3: { return FhgfsOpsIoctl_createFile(file, (void __user *) arg, 3); } break; case BEEGFS_IOC_TEST_IS_FHGFS: { return FhgfsOpsIoctl_testIsFhGFS(file, (void __user *) arg); } break; case BEEGFS_IOC_GET_RUNTIME_CFG_FILE: { /* get the virtual runtime client config file (path to procfs) */ return FhgfsOpsIoctl_getRuntimeCfgFile(file, (void __user *) arg); } break; case BEEGFS_IOC_GET_MOUNTID: { // get the mountID (e.g. for path in procfs) return FhgfsOpsIoctl_getMountID(file, (void __user *) arg); } break; case BEEGFS_IOC_GET_STRIPEINFO: { // get stripe info of a file return FhgfsOpsIoctl_getStripeInfo(file, (void __user *) arg); } break; case BEEGFS_IOC_GET_STRIPETARGET: { // get stripe info of a file return FhgfsOpsIoctl_getStripeTarget(file, (void __user *) arg); } break; case BEEGFS_IOC_GET_STRIPETARGET_V2: return FhgfsOpsIoctl_getStripeTargetV2(file, (void __user *) arg); case BEEGFS_IOC_MKFILE_STRIPEHINTS: { // create a file with stripe hints return FhgfsOpsIoctl_mkfileWithStripeHints(file, (void __user *) arg); } case BEEGFS_IOC_GETINODEID: { // calculate corresponding inodeID to given entryID return FhgfsOpsIoctl_getInodeID(file, (void __user *) arg); } case BEEGFS_IOC_GETENTRYINFO: { // return BeeGFS internal path to file return FhgfsOpsIoctl_getEntryInfo(file, (void __user *) arg); } case BEEGFS_IOC_PINGNODE: { return FhgfsOpsIoctl_pingNode(file, (void __user *) arg); } case TCGETS: { // filter isatty() test ioctl, which is often used by various standard tools return -ENOTTY; } break; default: { LOG_DEBUG_FORMATTED(log, Log_WARNING, logContext, "Unknown ioctl command code: %u", cmd); return -ENOIOCTLCMD; } break; } return 0; } #ifdef CONFIG_COMPAT /** * Compatibility ioctl method for 64-bit kernels called from 32-bit user space */ long FhgfsOpsIoctl_compatIoctl(struct file *file, unsigned int cmd, unsigned long arg) { struct dentry *dentry = file_dentry(file); App* app = FhgfsOps_getApp(dentry->d_sb); Logger* log = App_getLogger(app); const char* logContext = __func__; Logger_logFormatted(log, Log_SPAM, logContext, "Called ioctl cmd: %u", cmd); switch(cmd) { case BEEGFS_IOC32_GETVERSION_OLD: case BEEGFS_IOC32_GETVERSION: { cmd = BEEGFS_IOC_GETVERSION; } break; default: { return -ENOIOCTLCMD; } } return FhgfsOpsIoctl_ioctl(file, cmd, (unsigned long) compat_ptr(arg)); } #endif // CONFIG_COMPAT /** * Get the path to the client config file of this mount (e.g. /etc/fhgfs/fhgfs-client.conf). */ static long FhgfsOpsIoctl_getCfgFile(struct file *file, void __user *argp) { struct dentry *dentry = file_dentry(file); App* app = FhgfsOps_getApp(dentry->d_sb); Logger* log = App_getLogger(app); const char* logContext = __func__; struct BeegfsIoctl_GetCfgFile_Arg __user *confFile = (struct BeegfsIoctl_GetCfgFile_Arg*)argp; Config* cfg = App_getConfig(app); char* fileName = Config_getCfgFile(cfg); int cpRes; int cfgFileStrLen = strlen(fileName) + 1; // strlen() does not count \0 if (unlikely(cfgFileStrLen > BEEGFS_IOCTL_CFG_MAX_PATH)) { Logger_logFormatted(log, Log_WARNING, logContext, "Config file path too long (%d vs max %d)", cfgFileStrLen, BEEGFS_IOCTL_CFG_MAX_PATH); return -EINVAL; } if(!os_access_ok(VERIFY_WRITE, confFile, sizeof(*confFile) ) ) { Logger_logFormatted(log, Log_DEBUG, logContext, "access_ok() denied to write to conf_file"); return -EINVAL; } cpRes = copy_to_user(&confFile->path[0], fileName, cfgFileStrLen); // also calls access_ok if(cpRes) { LOG_DEBUG_FORMATTED(log, Log_DEBUG, logContext, "copy_to_user failed()"); return -EINVAL; } return 0; } /** * Get the path to the virtual runtime config file of this mount in procfs * (e.g. /proc/fs/beegfs//config). */ static long FhgfsOpsIoctl_getRuntimeCfgFile(struct file *file, void __user *argp) { struct dentry *dentry = file_dentry(file); App* app = FhgfsOps_getApp(dentry->d_sb); Logger* log = App_getLogger(app); const char* logContext = __func__; struct BeegfsIoctl_GetCfgFile_Arg __user *confFile = (struct BeegfsIoctl_GetCfgFile_Arg*)argp; char* fileName = vmalloc(BEEGFS_IOCTL_CFG_MAX_PATH); Node* localNode = App_getLocalNode(app); NodeString alias; int cpRes; int cfgFileStrLen; if(!os_access_ok(VERIFY_WRITE, confFile, sizeof(*confFile) ) ) { Logger_logFormatted(log, Log_DEBUG, logContext, "access_ok() denied to write to conf_file"); vfree(fileName); return -EINVAL; } Node_copyAlias(localNode, &alias); cfgFileStrLen = scnprintf(fileName, BEEGFS_IOCTL_CFG_MAX_PATH, "/proc/fs/beegfs/%s/config", alias.buf); if (cfgFileStrLen <= 0) { Logger_logFormatted(log, Log_WARNING, logContext, "buffer too small"); vfree(fileName); return -EINVAL; } cfgFileStrLen++; // scnprintf(...) does not count \0 cpRes = copy_to_user(&confFile->path[0], fileName, cfgFileStrLen); // also calls access_ok if(cpRes) { LOG_DEBUG_FORMATTED(log, Log_DEBUG, logContext, "copy_to_user failed()"); vfree(fileName); return -EINVAL; } vfree(fileName); return 0; } /** * Confirm to caller that we are a FhGFS mount. */ static long FhgfsOpsIoctl_testIsFhGFS(struct file *file, void __user *argp) { struct dentry *dentry = file_dentry(file); App* app = FhgfsOps_getApp(dentry->d_sb); Logger* log = App_getLogger(app); const char* logContext = __func__; int cpRes = copy_to_user(argp, BEEGFS_IOCTL_TEST_STRING, sizeof(BEEGFS_IOCTL_TEST_STRING) ); // (also calls access_ok) if(cpRes) { // result >0 is number of uncopied bytes LOG_DEBUG_FORMATTED(log, Log_WARNING, logContext, "copy_to_user failed()"); IGNORE_UNUSED_VARIABLE(log); IGNORE_UNUSED_VARIABLE(logContext); return -EINVAL; } return 0; } /** * Get the mountID aka clientID aka nodeID of client mount aka sessionID (aka alias :) */ static long FhgfsOpsIoctl_getMountID(struct file *file, void __user *argp) { struct dentry *dentry = file_dentry(file); App* app = FhgfsOps_getApp(dentry->d_sb); Logger* log = App_getLogger(app); const char* logContext = __func__; int cpRes; Node* localNode = App_getLocalNode(app); NodeString mountID; size_t mountIDStrLen; Node_copyAlias(localNode, &mountID); mountIDStrLen = strlen(mountID.buf); if(unlikely(mountIDStrLen > BEEGFS_IOCTL_MOUNTID_BUFLEN) ) { Logger_logFormatted(log, Log_WARNING, logContext, "unexpected: mountID too large for buffer (%d vs %d)", (int)BEEGFS_IOCTL_MOUNTID_BUFLEN, (int)mountIDStrLen+1); return -ENOBUFS; } cpRes = copy_to_user(argp, mountID.buf, mountIDStrLen+1); // (also calls access_ok) if(cpRes) { // result >0 is number of uncopied bytes LOG_DEBUG_FORMATTED(log, Log_WARNING, logContext, "copy_to_user failed()"); return -EINVAL; } return 0; } /** * Get stripe info of a file (chunksize etc.). */ static long FhgfsOpsIoctl_getStripeInfo(struct file *file, void __user *argp) { struct dentry* dentry = file_dentry(file); App* app = FhgfsOps_getApp(dentry->d_sb); Logger* log = App_getLogger(app); const char* logContext = __func__; struct inode* inode = file_inode(file); FhgfsInode* fhgfsInode = BEEGFS_INODE(inode); struct BeegfsIoctl_GetStripeInfo_Arg __user *getInfoArg = (struct BeegfsIoctl_GetStripeInfo_Arg*)argp; StripePattern* pattern = FhgfsInode_getStripePattern(fhgfsInode); uint16_t numTargets; int cpRes; if(S_ISDIR(inode->i_mode) ) { // directories have no patterns attached return -EISDIR; } if(!pattern) { // sanity check, should never happen (because file pattern is set during file open) Logger_logFormatted(log, Log_DEBUG, logContext, "Given file handle has no stripe pattern attached"); return -EINVAL; } // set pattern type cpRes = put_user(StripePattern_getPatternType(pattern), &getInfoArg->outPatternType); if(cpRes) return -EFAULT; // set chunksize cpRes = put_user(StripePattern_getChunkSize(pattern), &getInfoArg->outChunkSize); if(cpRes) return -EFAULT; // set number of stripe targets numTargets = UInt16Vec_length(pattern->getStripeTargetIDs(pattern) ); cpRes = put_user(numTargets, &getInfoArg->outNumTargets); if(cpRes) return -EFAULT; return 0; } static long resolveNodeToString(NodeStoreEx* nodes, uint32_t nodeID, char __user* nodeStrID, Logger* log) { NodeString nodeAlias; NumNodeID numID = { nodeID }; size_t nodeIDLen; long retVal = 0; Node* node = NodeStoreEx_referenceNode(nodes, numID); if (!node) { // node not found in store: set empty string as result if (copy_to_user(nodeStrID, "", 1)) { LOG_DEBUG_FORMATTED(log, Log_DEBUG, __func__, "copy_to_user failed()"); return -EINVAL; } return 0; } Node_copyAlias(node, &nodeAlias); nodeIDLen = strlen(nodeAlias.buf) + 1; if (nodeIDLen > BEEGFS_IOCTL_NODEALIAS_BUFLEN) { // nodeID too large for buffer retVal = -ENOBUFS; goto out; } if (copy_to_user(nodeStrID, nodeAlias.buf, nodeIDLen)) retVal = -EFAULT; out: Node_put(node); return retVal; } static long getStripePatternImpl(struct file* file, uint32_t targetIdx, uint32_t* targetOrGroup, uint32_t* primaryTarget, uint32_t* secondaryTarget, uint32_t* primaryNodeID, uint32_t* secondaryNodeID, char __user* primaryNodeStrID, char __user* secondaryNodeStrID) { struct dentry* dentry = file_dentry(file); App* app = FhgfsOps_getApp(dentry->d_sb); Logger* log = App_getLogger(app); const char* logContext = __func__; TargetMapper* targetMapper = App_getTargetMapper(app); NodeStoreEx* storageNodes = App_getStorageNodes(app); struct inode* inode = dentry->d_inode; FhgfsInode* fhgfsInode = BEEGFS_INODE(inode); StripePattern* pattern = FhgfsInode_getStripePattern(fhgfsInode); long retVal = 0; size_t numTargets; // directories have no patterns attached if (S_ISDIR(inode->i_mode)) return -EISDIR; if (!pattern) { // sanity check, should never happen (because file pattern is set during file open) Logger_logFormatted(log, Log_DEBUG, logContext, "Given file handle has no stripe pattern attached"); return -EINVAL; } // check if wanted target index is valid numTargets = UInt16Vec_length(pattern->getStripeTargetIDs(pattern)); if (targetIdx >= numTargets) return -EINVAL; // set targetID *targetOrGroup = UInt16Vec_at(pattern->getStripeTargetIDs(pattern), targetIdx); // resolve buddy group if necessary if (pattern->patternType == STRIPEPATTERN_BuddyMirror) { *primaryTarget = MirrorBuddyGroupMapper_getPrimaryTargetID(app->storageBuddyGroupMapper, *targetOrGroup); *secondaryTarget = MirrorBuddyGroupMapper_getSecondaryTargetID(app->storageBuddyGroupMapper, *targetOrGroup); } else { *primaryTarget = 0; *secondaryTarget = 0; } // resolve targets to nodes *primaryNodeID = TargetMapper_getNodeID(targetMapper, *primaryTarget ? *primaryTarget : *targetOrGroup).value; *secondaryNodeID = *secondaryTarget ? TargetMapper_getNodeID(targetMapper, *secondaryTarget).value : 0; // resolve node ID strings retVal = resolveNodeToString(storageNodes, *primaryNodeID, primaryNodeStrID, log); if (retVal) goto out; if (secondaryNodeStrID && *secondaryNodeID) retVal = resolveNodeToString(storageNodes, *secondaryNodeID, secondaryNodeStrID, log); out: return retVal; } /** * Get stripe target of a file (index-based). */ static long FhgfsOpsIoctl_getStripeTarget(struct file *file, void __user *argp) { struct BeegfsIoctl_GetStripeTarget_Arg __user* arg = argp; uint32_t targetOrGroup; uint32_t primaryTarget; uint32_t secondaryTarget; uint32_t primaryNodeID; uint32_t secondaryNodeID; uint16_t wantedTargetIndex; long retVal = 0; if (get_user(wantedTargetIndex, &arg->targetIndex)) return -EFAULT; retVal = getStripePatternImpl(file, wantedTargetIndex, &targetOrGroup, &primaryTarget, &secondaryTarget, &primaryNodeID, &secondaryNodeID, arg->outNodeAlias, NULL); if (retVal) return retVal; if (put_user(targetOrGroup, &arg->outTargetNumID)) return -EFAULT; if (put_user(primaryNodeID, &arg->outNodeNumID)) return -EFAULT; return 0; } static long FhgfsOpsIoctl_getStripeTargetV2(struct file *file, void __user *argp) { struct BeegfsIoctl_GetStripeTargetV2_Arg __user* arg = argp; uint32_t targetOrGroup; uint32_t primaryTarget; uint32_t secondaryTarget; uint32_t primaryNodeID; uint32_t secondaryNodeID; uint32_t wantedTargetIndex; long retVal = 0; if (get_user(wantedTargetIndex, &arg->targetIndex)) return -EFAULT; retVal = getStripePatternImpl(file, wantedTargetIndex, &targetOrGroup, &primaryTarget, &secondaryTarget, &primaryNodeID, &secondaryNodeID, arg->primaryNodeAlias, arg->secondaryNodeAlias); if (retVal) return retVal; if (put_user(targetOrGroup, &arg->targetOrGroup)) return -EFAULT; if (put_user(primaryTarget, &arg->primaryTarget)) return -EFAULT; if (put_user(secondaryTarget, &arg->secondaryTarget)) return -EFAULT; if (put_user(primaryNodeID, &arg->primaryNodeID)) return -EFAULT; if (put_user(secondaryNodeID, &arg->secondaryNodeID)) return -EFAULT; return 0; } /** * Create a new regular file with stripe hints (chunksize, numtargets). * * @param file parent directory of the new file */ long FhgfsOpsIoctl_mkfileWithStripeHints(struct file *file, void __user *argp) { struct dentry* dentry = file_dentry(file); struct inode* parentInode = file_inode(file); FhgfsInode* fhgfsParentInode = BEEGFS_INODE(parentInode); App* app = FhgfsOps_getApp(dentry->d_sb); Logger* log = App_getLogger(app); const char* logContext = __func__; const int umask = current_umask(); struct BeegfsIoctl_MkFileWithStripeHints_Arg __user *mkfileArg = (struct BeegfsIoctl_MkFileWithStripeHints_Arg*)argp; long retVal; const char __user* userFilename; char* filename; int mode; // mode (permission) of the new file unsigned numtargets; // number of desired targets, 0 for directory default unsigned chunksize; // must be 64K or multiple of 64K, 0 for directory default int cpRes; const EntryInfo* parentEntryInfo; FhgfsOpsErr mkRes; struct FileEvent event = FILEEVENT_EMPTY; struct FileEvent* eventSent = NULL; struct CreateInfo createInfo = { .preferredStorageTargets = NULL, .preferredMetaTargets = NULL }; #ifdef KERNEL_HAS_FILE_F_VFSMNT struct vfsmount* mnt = file->f_vfsmnt; #else struct vfsmount* mnt = file->f_path.mnt; #endif Logger_logFormatted(log, Log_SPAM, logContext, "Create file from ioctl"); if(!S_ISDIR(parentInode->i_mode) ) { // given inode does not refer to a directory return -ENOTDIR; } retVal = os_generic_permission(parentInode, MAY_WRITE | MAY_EXEC); if (retVal) return retVal; // copy mode cpRes = get_user(mode, &mkfileArg->mode); if(cpRes) return -EFAULT; // make sure we only use permissions bits of given mode and set regular file as format bit mode = (mode & (S_IRWXU | S_IRWXG | S_IRWXO) ) | S_IFREG; // copy numtargets cpRes = get_user(numtargets, &mkfileArg->numtargets); if(cpRes) return -EFAULT; // copy chunksize cpRes = get_user(chunksize, &mkfileArg->chunksize); if(cpRes) return -EFAULT; if (get_user(userFilename, &mkfileArg->filename)) return -EFAULT; if (chunksize != 0) { // check if chunksize is valid if(unlikely( (chunksize < STRIPEPATTERN_MIN_CHUNKSIZE) || !MathTk_isPowerOfTwo(chunksize) ) ) return -EINVAL; // chunksize is not a multiple of 64Ki } // check and reference mnt write counter retVal = mnt_want_write(mnt); if(retVal) return retVal; // copy filename filename = strndup_user(userFilename, BEEGFS_IOCTL_FILENAME_MAXLEN); if(IS_ERR(filename) ) { retVal = PTR_ERR(filename); goto err_cleanup_lock; } if (app->cfg->eventLogMask & EventLogMask_LINK_OP) { // we don't have a dentry for the target file here. we could use the dentry to the directory // and the requested file name, but decided to not bother yet because ioctl operations are // expected to be so rare. FileEvent_init(&event, FileEventType_CREATE, NULL); eventSent = &event; } CreateInfo_init(app, parentInode, filename, mode, umask, true, eventSent, &createInfo); FhgfsInode_entryInfoReadLock(fhgfsParentInode); // L O C K EntryInfo parentEntryInfo = FhgfsInode_getEntryInfo(fhgfsParentInode); mkRes = FhgfsOpsRemoting_mkfileWithStripeHints(app, parentEntryInfo, &createInfo, numtargets, chunksize, NULL); FhgfsInode_entryInfoReadUnlock(fhgfsParentInode); // U N L O C K EntryInfo if(mkRes != FhgfsOpsErr_SUCCESS) { retVal = FhgfsOpsErr_toSysErr(mkRes); // (note: newEntryInfo was not alloc'ed) goto err_cleanup_filename; } retVal = 0; err_cleanup_filename: // cleanup kfree(filename); FileEvent_uninit(&event); err_cleanup_lock: mnt_drop_write(mnt); // release mnt write reference counter return retVal; } /** * create a file with special settings (such as preferred targets). * * @return 0 on success, negative linux error code otherwise */ static long FhgfsOpsIoctl_createFile(struct file *file, void __user *argp, int version) { struct dentry *dentry = file_dentry(file); struct inode *inode = file_inode(file); App* app = FhgfsOps_getApp(dentry->d_sb); Logger* log = App_getLogger(app); const char* logContext = __func__; const int umask = current_umask(); StoragePoolId storagePoolId; EntryInfo parentInfo; struct BeegfsIoctl_MkFileV3_Arg fileInfo; struct CreateInfo createInfo = { .preferredStorageTargets = NULL, .preferredMetaTargets = NULL }; int retVal = 0; FhgfsOpsErr mkRes; struct FileEvent event = FILEEVENT_EMPTY; const struct FileEvent* eventSent = NULL; #ifdef KERNEL_HAS_FILE_F_VFSMNT struct vfsmount* mnt = file->f_vfsmnt; #else struct vfsmount* mnt = file->f_path.mnt; #endif Logger_logFormatted(log, Log_SPAM, logContext, "Create file from ioctl"); retVal = os_generic_permission(inode, MAY_WRITE | MAY_EXEC); if (retVal) return retVal; retVal = mnt_want_write(mnt); // check and rw-reference counter if (retVal) return retVal; memset(&fileInfo, 0, sizeof(struct BeegfsIoctl_MkFileV3_Arg)); switch(version) { case 1: retVal = IoctlHelper_ioctlCreateFileCopyFromUser(app, argp, &fileInfo); break; case 2: retVal = IoctlHelper_ioctlCreateFileCopyFromUserV2(app, argp, &fileInfo); break; case 3: retVal = IoctlHelper_ioctlCreateFileCopyFromUserV3(app, argp, &fileInfo); break; default: retVal = IoctlHelper_ioctlCreateFileCopyFromUser(app, argp, &fileInfo); break; } if(retVal) { LOG_DEBUG_FORMATTED(log, Log_SPAM, logContext, "Copy from user of struct Fhgfs_ioctlMkFile failed"); goto cleanup; } // the actual event is initialized appropriatly later if (app->cfg->eventLogMask & EventLogMask_LINK_OP) eventSent = &event; CreateInfo_init(app, inode, fileInfo.entryName, fileInfo.mode, umask, true, eventSent, &createInfo); StoragePoolId_set(&storagePoolId, fileInfo.storagePoolId); CreateInfo_setStoragePoolId(&createInfo, storagePoolId); retVal = IoctlHelper_ioctlCreateFileTargetsToList(app, &fileInfo, &createInfo); // target list if (retVal) goto cleanup; // only use the provided UID and GID if we are root /* note: this means we create the file with different uid/gid without notifying the caller. maybe we should change that. */ if (FhgfsCommon_getCurrentUserID() == 0 || FhgfsCommon_getCurrentGroupID() == 0) { createInfo.userID = fileInfo.uid; createInfo.groupID = fileInfo.gid; } if (fileInfo.parentIsBuddyMirrored) EntryInfo_init(&parentInfo, NodeOrGroup_fromGroup(fileInfo.ownerNodeID), fileInfo.parentParentEntryID, fileInfo.parentEntryID, fileInfo.parentName, DirEntryType_DIRECTORY, STATFLAG_HINT_INLINE); else { NumNodeID nodeID; NumNodeID_set(&nodeID, fileInfo.ownerNodeID); EntryInfo_init(&parentInfo, NodeOrGroup_fromNode(nodeID), fileInfo.parentParentEntryID, fileInfo.parentEntryID, fileInfo.parentName, DirEntryType_DIRECTORY, STATFLAG_HINT_INLINE); } switch (fileInfo.fileType) { case DT_REG: { // we don't have a dentry for the target file here. we could use the dentry to the directory // and the requested file name, but decided to not bother yet because ioctl operations are // expected to be so rare. if (app->cfg->eventLogMask & EventLogMask_LINK_OP) FileEvent_init(&event, FileEventType_CREATE, NULL); down_read(&inode->i_sb->s_umount); LOG_DEBUG_FORMATTED(log, Log_SPAM, logContext, "Going to create file %s", createInfo.entryName); mkRes = FhgfsOpsRemoting_mkfile(app, &parentInfo, &createInfo, NULL); if(mkRes != FhgfsOpsErr_SUCCESS) { // failure (note: no need to care about newEntryInfo, it was not allocated) LOG_DEBUG_FORMATTED(log, Log_SPAM, logContext, "mkfile failed"); retVal = FhgfsOpsErr_toSysErr(mkRes); } up_read(&inode->i_sb->s_umount); FileEvent_uninit(&event); } break; case DT_LNK: { EntryInfo newEntryInfo; // only needed as mandatory argument if (!fileInfo.symlinkTo) { retVal = -EINVAL; goto cleanup; } if (app->cfg->eventLogMask & EventLogMask_LINK_OP) { event.eventType = FileEventType_SYMLINK; FileEvent_setTargetStr(&event, fileInfo.symlinkTo); } down_read(&inode->i_sb->s_umount); // destroys &event mkRes = FhgfsOpsHelper_symlink(app, &parentInfo, fileInfo.symlinkTo, &createInfo, &newEntryInfo); if(mkRes != FhgfsOpsErr_SUCCESS) { // failed (note: no need to care about newEntryInfo, it was not allocated) LOG_DEBUG_FORMATTED(log, Log_SPAM, logContext, "mkfile failed"); retVal = FhgfsOpsErr_toSysErr(mkRes); } else EntryInfo_uninit(&newEntryInfo); // unit newEntryInfo, we don't need it up_read(&inode->i_sb->s_umount); } break; default: { // unsupported file type LOG_DEBUG_FORMATTED(log, Log_NOTICE, logContext, "Unsupported file type: %d", fileInfo.fileType); retVal = -EINVAL; goto cleanup; } break; } cleanup: SAFE_KFREE(fileInfo.parentParentEntryID); SAFE_KFREE(fileInfo.parentEntryID); SAFE_KFREE(fileInfo.parentName); SAFE_KFREE(fileInfo.entryName); SAFE_KFREE(fileInfo.symlinkTo); SAFE_KFREE(fileInfo.prefTargets); if (createInfo.preferredStorageTargets && createInfo.preferredStorageTargets != App_getPreferredStorageTargets(app) ) { UInt16List_uninit(createInfo.preferredStorageTargets); SAFE_KFREE(createInfo.preferredStorageTargets); } if (createInfo.preferredMetaTargets && createInfo.preferredMetaTargets != App_getPreferredMetaNodes(app) ) { UInt16List_uninit(createInfo.preferredMetaTargets); SAFE_KFREE(createInfo.preferredMetaTargets); } mnt_drop_write(mnt); // release the rw-reference counter return retVal; } static long FhgfsOpsIoctl_getInodeID(struct file *file, void __user *argp) { struct BeegfsIoctl_GetInodeID_Arg __user *getInodeIDArg = argp; struct super_block* superBlock = file_dentry(file)->d_sb; Logger* log = App_getLogger(FhgfsOps_getApp(superBlock)); uint64_t inodeID; char entryID[BEEGFS_IOCTL_ENTRYID_MAXLEN + 1]; if (copy_from_user(entryID, &getInodeIDArg->entryID, sizeof(entryID))) { Logger_logFormatted(log, Log_DEBUG, __func__, "Copying entryID from userspace memory failed."); return -EFAULT; } inodeID = FhgfsInode_generateInodeID(superBlock, entryID, strnlen(entryID, sizeof(entryID))); if (put_user(inodeID, &getInodeIDArg->inodeID)) { Logger_logFormatted(log, Log_DEBUG, __func__, "Copying inodeID to userspace memory failed."); return -EFAULT; } return 0; } static long FhgfsOpsIoctl_pingNode(struct file *file, void __user *argp) { struct super_block* superBlock = file_dentry(file)->d_sb; App* app = FhgfsOps_getApp(superBlock); Logger* log = App_getLogger(app); NoAllocBufferStore* bufStore = App_getMsgBufStore(app); NodeStoreEx* nodeStore; Node* node; NodeString alias; NodeConnPool* connPool; Socket* sock = NULL; struct BeegfsIoctl_PingNode_Arg __user *pingArg = argp; struct BeegfsIoctl_PingNode_Arg ping; NumNodeID nodeId; long rc; int i; NodeType nodeType; if (copy_from_user(&ping.params, &pingArg->params, sizeof(ping.params))) { Logger_logFormatted(log, Log_ERR, __func__, "Copying ping arguments from userspace memory failed."); return -EFAULT; } ping.params.nodeType[BEEGFS_IOCTL_NODETYPE_BUFLEN - 1] = 0; Logger_logFormatted(log, Log_DEBUG, __func__, "nodeId: %u nodeType: %s count=%u", ping.params.nodeId, ping.params.nodeType, ping.params.count); memset(&ping.results, 0, sizeof(ping.results)); if (ping.params.count > BEEGFS_IOCTL_PING_MAX_COUNT) { Logger_logFormatted(log, Log_ERR, __func__, "count too high, max is %d", BEEGFS_IOCTL_PING_MAX_COUNT); return -EINVAL; } if (ping.params.interval > BEEGFS_IOCTL_PING_MAX_INTERVAL) { Logger_logFormatted(log, Log_ERR, __func__, "interval too high, max is %d", BEEGFS_IOCTL_PING_MAX_INTERVAL); return -EINVAL; } NumNodeID_set(&nodeId, ping.params.nodeId); nodeType = FhgfsOpsIoctl_strToNodeType(ping.params.nodeType, sizeof(ping.params.nodeType)); switch (nodeType) { case NODETYPE_Meta: nodeStore = App_getMetaNodes(app); break; case NODETYPE_Storage: nodeStore = App_getStorageNodes(app); break; case NODETYPE_Mgmt: nodeStore = App_getMgmtNodes(app); break; default: Logger_logFormatted(log, Log_ERR, __func__, "Invalid nodetype: %s", ping.params.nodeType); return -EINVAL; } node = NodeStoreEx_referenceNode(nodeStore, nodeId); if (node == NULL) { Logger_logFormatted(log, Log_ERR, __func__, "Node not found. nodeId: %u nodeType: %s", ping.params.nodeId, ping.params.nodeType); return -EINVAL; } Node_copyAlias(node, &alias); StringTk_strncpyTerminated(ping.results.outNode, alias.buf, sizeof(ping.results.outNode)); connPool = Node_getConnPool(node); rc = 0; for (i = 0; i <= ping.params.count; ++i) { HeartbeatMsgEx* rspMsgEx; char* respBuf = NULL; NetMessage* respMsg = NULL; HeartbeatRequestMsgEx msg; FhgfsOpsErr requestRes; Time startTime; Time endTime; NumNodeID resNodeID; int resNodeType; if (i > 0) msleep(ping.params.interval); if (sock == NULL) { sock = NodeConnPool_acquireStreamSocket(connPool); if (sock == NULL) { Logger_logFormatted(log, Log_ERR, __func__, "Null socket from connPool"); rc = -EINVAL; goto next_iter; } } HeartbeatRequestMsgEx_init(&msg); Time_init(&startTime); requestRes = MessagingTk_requestResponseSock(app, node, (NetMessage*)&msg, NETMSGTYPE_Heartbeat, &respBuf, &respMsg, sock); Time_init(&endTime); if (requestRes != FhgfsOpsErr_SUCCESS) { NodeConnPool_invalidateStreamSocket(connPool, sock); sock = NULL; ping.results.outErrors++; goto next_iter; } // Skip the first one because it may have connection overhead. if (i == 0) goto next_iter; rspMsgEx = (HeartbeatMsgEx*) respMsg; resNodeID = HeartbeatMsgEx_getNodeNumID(rspMsgEx); resNodeType = HeartbeatMsgEx_getNodeType(rspMsgEx); if (!NumNodeID_compare(&resNodeID, &nodeId)) { Logger_logFormatted(log, Log_ERR, __func__, "Wrong node ID returned from heartbeat: %s", NumNodeID_str(&resNodeID)); rc = -EPROTO; } else if (resNodeType != (int) nodeType) { Logger_logFormatted(log, Log_ERR, __func__, "Wrong node type returned from heartbeat: %d", resNodeType); rc = -EPROTO; } else { unsigned elapsed = Time_elapsedSinceNS(&endTime, &startTime); ping.results.outSuccess++; ping.results.outPingTime[i - 1] = elapsed; ping.results.outTotalTime += elapsed; StringTk_strncpyTerminated(ping.results.outPingType[i - 1], NIC_nicTypeToString(Socket_getSockType(sock)), sizeof(ping.results.outPingType[i - 1])); } next_iter: if (respMsg) NETMESSAGE_FREE(respMsg); if (respBuf) NoAllocBufferStore_addBuf(bufStore, respBuf); if (rc != 0) break; } if (sock != NULL) NodeConnPool_releaseStreamSocket(connPool, sock); Node_put(node); if (copy_to_user(&pingArg->results, &ping.results, sizeof(ping.results))) { Logger_logFormatted(log, Log_ERR, __func__, "Copying ping results to userspace memory failed."); rc = -EFAULT; } return rc; } /** * Get EntryInfo data for given file. * * @return 0 on success, negative linux error code otherwise */ static long FhgfsOpsIoctl_getEntryInfo(struct file *file, void __user *argp) { struct BeegfsIoctl_GetEntryInfo_Arg __user *arg = argp; struct dentry* fileDentry = file_dentry(file); App* app = FhgfsOps_getApp(fileDentry->d_sb); Logger* log = App_getLogger(app); FhgfsInode* beegfsInode = BEEGFS_INODE(fileDentry->d_inode); const EntryInfo* entryInfo; FhgfsInode_entryInfoReadLock(beegfsInode); entryInfo = FhgfsInode_getEntryInfo(beegfsInode); if (put_user(EntryInfo_getOwner(entryInfo), &arg->ownerID)) goto fail; if (copy_to_user(arg->parentEntryID, EntryInfo_getParentEntryID(entryInfo), strnlen(entryInfo->parentEntryID, BEEGFS_IOCTL_ENTRYID_MAXLEN) + 1)) goto fail; if (copy_to_user(arg->entryID, EntryInfo_getEntryID(entryInfo), strnlen(entryInfo->entryID, BEEGFS_IOCTL_ENTRYID_MAXLEN) + 1)) goto fail; if (put_user(entryInfo->entryType, &arg->entryType)) goto fail; if (put_user(entryInfo->featureFlags, &arg->featureFlags)) goto fail; FhgfsInode_entryInfoReadUnlock(beegfsInode); return 0; fail: FhgfsInode_entryInfoReadUnlock(beegfsInode); Logger_logFormatted(log, Log_DEBUG, __func__, "Copying entryInfo to userspace memory failed."); return -EFAULT; }