text: /*
   * Ioctl helper functions
   */
  
  #include <app/log/Logger.h>
  #include <common/toolkit/list/UInt16List.h>
  #include "IoctlHelper.h"
  
  #define STRDUP_OR_RETURN(target, source, len, name) \
     do { \
        if ((len) == 0) \
           return -EINVAL; \
        (target) = strndup_user((const char __user *) (source), (len)); \
        if (IS_ERR((target))) \
        { \
           int error = PTR_ERR((target)); \
           (target) = NULL; \
           LOG_DEBUG_FORMATTED(log, Log_NOTICE, logContext, "Invalid " name " string"); \
           return error; \
        } \
     } while (0)
  
  /**
   * Copy struct BeegfsIoctl_MkFile_Arg from user to kernel space
   *
   * Note: For simplicity, the calling function needs to free alloced outFileInfo members, even in
   * case of errors. Therefore, *outFileInfo is supposed to be memset() with 0 by the caller.
   *
   * @return 0 on success, negative linux error code otherwise
   */
  long IoctlHelper_ioctlCreateFileCopyFromUser(App* app, void __user *argp,
     struct BeegfsIoctl_MkFileV3_Arg* outFileInfo)
  {
     Logger* log = App_getLogger(app);
     const char* logContext = __func__;
     struct BeegfsIoctl_MkFile_Arg userFileInfo; // fileInfo still with pointers into user space
  
     memset(&userFileInfo, 0, sizeof(userFileInfo) ); // avoid clang warning
  
     if (copy_from_user(&userFileInfo, argp, sizeof(userFileInfo) ) )
        return -EFAULT;
  
     /* we cannot simply do: outFileInfo = userFileInfo, as that would overwrite all NULL pointers
      * and we use those NULL pointers to simplify free(), so we copy integers one by one */
     outFileInfo->ownerNodeID.value      = userFileInfo.ownerNodeID;
     outFileInfo->parentParentEntryIDLen = userFileInfo.parentParentEntryIDLen;
     outFileInfo->parentEntryIDLen       = userFileInfo.parentEntryIDLen;
     outFileInfo->parentNameLen          = userFileInfo.parentNameLen;
     outFileInfo->entryNameLen           = userFileInfo.entryNameLen;
     outFileInfo->symlinkToLen           = userFileInfo.symlinkToLen;
     outFileInfo->mode                   = userFileInfo.mode;
     outFileInfo->uid                    = userFileInfo.uid;
     outFileInfo->gid                    = userFileInfo.gid;
     outFileInfo->numTargets             = userFileInfo.numTargets;
     outFileInfo->prefTargetsLen         = userFileInfo.prefTargetsLen;
     outFileInfo->fileType               = userFileInfo.fileType;
     outFileInfo->parentIsBuddyMirrored  = false;
  
     StoragePoolId_set(&(outFileInfo->storagePoolId), STORAGEPOOLID_INVALIDPOOLID);
  
     /* Now copy and alloc all char* to kernel space */
  
     STRDUP_OR_RETURN(outFileInfo->parentParentEntryID, userFileInfo.parentParentEntryID,
           outFileInfo->parentParentEntryIDLen, "parentParentEntryID");
     STRDUP_OR_RETURN(outFileInfo->parentEntryID, userFileInfo.parentEntryID,
           outFileInfo->parentEntryIDLen, "parentEntryID");
     STRDUP_OR_RETURN(outFileInfo->parentName, userFileInfo.parentName, outFileInfo->parentNameLen,
           "parentName");
     STRDUP_OR_RETURN(outFileInfo->entryName, userFileInfo.entryName, outFileInfo->entryNameLen,
           "entryName");
  
     if (userFileInfo.fileType == DT_LNK && userFileInfo.symlinkToLen > 0)
        STRDUP_OR_RETURN(outFileInfo->symlinkTo, userFileInfo.symlinkTo, outFileInfo->symlinkToLen,
              "symlinkTo");
  
     // copy prefTargets array and verify it...
  
     /* Note: try not to exceed a page, as kmalloc might fail for high order allocations. However,
      *       that should only happen with very high number of stripes and we will limit the number
      *       in fhgfs-ctl. */
  
     // check if given num targets actually fits inside given targets array
     // (note: +1 for terminating 0 element)
     if(outFileInfo->prefTargetsLen < ( (outFileInfo->numTargets + 1) * (int) sizeof(uint16_t) ) )
     {
        Logger_logFormatted(log, Log_WARNING, logContext,
           "prefTargetsLen(=%d) too small for given numTargets(=%d+1)",
           outFileInfo->prefTargetsLen,
           outFileInfo->numTargets);
        return -EINVAL;
     }
  
     // copy prefTargets to kernel buffer based on raw prefTargetsLen
     outFileInfo->prefTargets = memdup_user( (char __user *) userFileInfo.prefTargets,
        outFileInfo->prefTargetsLen);
     if (IS_ERR(outFileInfo->prefTargets) )
     {
        int error = PTR_ERR(outFileInfo->prefTargets);
        LOG_DEBUG_FORMATTED(log, Log_NOTICE, logContext, "Unable to copy prefTargets array");
        outFileInfo->prefTargets = NULL;
        return error;
     }
  
     // check if prefTargets given by user space has a terminating zero as last array element
     // (note: not +1, because numTargets count does not include terminating zero element)
     if(outFileInfo->prefTargets[outFileInfo->numTargets] != 0)
     {
        Logger_logFormatted(log, Log_WARNING, logContext, "prefTargets array is not zero-terminated");
        return -EINVAL;
     }
  
  
     return 0;
  }
  
  long IoctlHelper_ioctlCreateFileCopyFromUserV2(App* app, void __user *argp,
     struct BeegfsIoctl_MkFileV3_Arg* outFileInfo)
  {
     Logger* log = App_getLogger(app);
     const char* logContext = __func__;
     struct BeegfsIoctl_MkFileV2_Arg userFileInfo; // fileInfo still with pointers into user space
  
     memset(&userFileInfo, 0, sizeof(userFileInfo)); // avoid clang warning
  
     if (copy_from_user(&userFileInfo, argp, sizeof(userFileInfo)))
        return -EFAULT;
  
     /* we cannot simply do: outFileInfo = userFileInfo, as that would overwrite all NULL pointers
      * and we use those NULL pointers to simplify free(), so we copy integers one by one */
     outFileInfo->ownerNodeID            = userFileInfo.ownerNodeID;
     outFileInfo->parentParentEntryIDLen = userFileInfo.parentParentEntryIDLen;
     outFileInfo->parentEntryIDLen       = userFileInfo.parentEntryIDLen;
     outFileInfo->parentNameLen          = userFileInfo.parentNameLen;
     outFileInfo->entryNameLen           = userFileInfo.entryNameLen;
     outFileInfo->symlinkToLen           = userFileInfo.symlinkToLen;
     outFileInfo->mode                   = userFileInfo.mode;
     outFileInfo->uid                    = userFileInfo.uid;
     outFileInfo->gid                    = userFileInfo.gid;
     outFileInfo->numTargets             = userFileInfo.numTargets;
     outFileInfo->prefTargetsLen         = userFileInfo.prefTargetsLen;
     outFileInfo->fileType               = userFileInfo.fileType;
     outFileInfo->parentIsBuddyMirrored  = userFileInfo.parentIsBuddyMirrored;
     StoragePoolId_set(&(outFileInfo->storagePoolId), STORAGEPOOLID_INVALIDPOOLID);
  
     /* Now copy and alloc all char* to kernel space */
  
     STRDUP_OR_RETURN(outFileInfo->parentParentEntryID, userFileInfo.parentParentEntryID,
           outFileInfo->parentParentEntryIDLen, "parentParentEntryID");
     STRDUP_OR_RETURN(outFileInfo->parentEntryID, userFileInfo.parentEntryID,
           outFileInfo->parentEntryIDLen, "parentEntryID");
     STRDUP_OR_RETURN(outFileInfo->parentName, userFileInfo.parentName, outFileInfo->parentNameLen,
           "parentName");
     STRDUP_OR_RETURN(outFileInfo->entryName, userFileInfo.entryName, outFileInfo->entryNameLen,
           "entryName");
  
     if (userFileInfo.fileType == DT_LNK && userFileInfo.symlinkToLen > 0)
        STRDUP_OR_RETURN(outFileInfo->symlinkTo, userFileInfo.symlinkTo, outFileInfo->symlinkToLen,
              "symlinkTo");
  
     // copy prefTargets array and verify it...
  
     /* Note: try not to exceed a page, as kmalloc might fail for high order allocations. However,
      *       that should only happen with very high number of stripes and we will limit the number
      *       in fhgfs-ctl. */
  
     // check if given num targets actually fits inside given targets array
     // (note: +1 for terminating 0 element)
     if(outFileInfo->prefTargetsLen < ( (outFileInfo->numTargets + 1) * (int) sizeof(uint16_t) ) )
     {
        Logger_logFormatted(log, Log_WARNING, logContext,
           "prefTargetsLen(=%d) too small for given numTargets(=%d+1)",
           outFileInfo->prefTargetsLen,
           outFileInfo->numTargets);
        return -EINVAL;
     }
  
     // copy prefTargets to kernel buffer based on raw prefTargetsLen
     outFileInfo->prefTargets = memdup_user( (char __user *) userFileInfo.prefTargets,
        outFileInfo->prefTargetsLen);
     if (IS_ERR(outFileInfo->prefTargets) )
     {
        int error = PTR_ERR(outFileInfo->prefTargets);
        LOG_DEBUG_FORMATTED(log, Log_NOTICE, logContext, "Unable to copy prefTargets array");
        outFileInfo->prefTargets = NULL;
        return error;
     }
  
     // check if prefTargets given by user space has a terminating zero as last array element
     // (note: not +1, because numTargets count does not include terminating zero element)
     if(outFileInfo->prefTargets[outFileInfo->numTargets] != 0)
     {
        Logger_logFormatted(log, Log_WARNING, logContext, "prefTargets array is not zero-terminated");
        return -EINVAL;
     }
  
  
     return 0;
  }
  
  long IoctlHelper_ioctlCreateFileCopyFromUserV3(App* app, void __user *argp,
     struct BeegfsIoctl_MkFileV3_Arg* outFileInfo)
  {
     Logger* log = App_getLogger(app);
     const char* logContext = __func__;
     struct BeegfsIoctl_MkFileV3_Arg userFileInfo; // fileInfo still with pointers into user space
  
     memset(&userFileInfo, 0, sizeof(userFileInfo)); // avoid clang warning
  
     if (copy_from_user(&userFileInfo, argp, sizeof(userFileInfo)))
        return -EFAULT;
  
     /* we cannot simply do: outFileInfo = userFileInfo, as that would overwrite all NULL pointers
      * and we use those NULL pointers to simplify free(), so we copy integers one by one */
     outFileInfo->ownerNodeID            = userFileInfo.ownerNodeID;
     outFileInfo->parentParentEntryIDLen = userFileInfo.parentParentEntryIDLen;
     outFileInfo->parentEntryIDLen       = userFileInfo.parentEntryIDLen;
     outFileInfo->parentNameLen          = userFileInfo.parentNameLen;
     outFileInfo->entryNameLen           = userFileInfo.entryNameLen;
     outFileInfo->symlinkToLen           = userFileInfo.symlinkToLen;
     outFileInfo->mode                   = userFileInfo.mode;
     outFileInfo->uid                    = userFileInfo.uid;
     outFileInfo->gid                    = userFileInfo.gid;
     outFileInfo->numTargets             = userFileInfo.numTargets;
     outFileInfo->prefTargetsLen         = userFileInfo.prefTargetsLen;
     outFileInfo->fileType               = userFileInfo.fileType;
     outFileInfo->parentIsBuddyMirrored  = userFileInfo.parentIsBuddyMirrored;
     outFileInfo->storagePoolId          = userFileInfo.storagePoolId;
  
     /* Now copy and alloc all char* to kernel space */
  
     STRDUP_OR_RETURN(outFileInfo->parentParentEntryID, userFileInfo.parentParentEntryID,
           outFileInfo->parentParentEntryIDLen, "parentParentEntryID");
     STRDUP_OR_RETURN(outFileInfo->parentEntryID, userFileInfo.parentEntryID,
           outFileInfo->parentEntryIDLen, "parentEntryID");
     STRDUP_OR_RETURN(outFileInfo->parentName, userFileInfo.parentName, outFileInfo->parentNameLen,
           "parentName");
     STRDUP_OR_RETURN(outFileInfo->entryName, userFileInfo.entryName, outFileInfo->entryNameLen,
           "entryName");
  
     if (userFileInfo.fileType == DT_LNK && userFileInfo.symlinkToLen > 0)
        STRDUP_OR_RETURN(outFileInfo->symlinkTo, userFileInfo.symlinkTo, outFileInfo->symlinkToLen,
              "symlinkTo");
  
     // copy prefTargets array and verify it...
  
     /* Note: try not to exceed a page, as kmalloc might fail for high order allocations. However,
      *       that should only happen with very high number of stripes and we will limit the number
      *       in fhgfs-ctl. */
  
     // check if given num targets actually fits inside given targets array
     // (note: +1 for terminating 0 element)
     if(outFileInfo->prefTargetsLen < ( (outFileInfo->numTargets + 1) * (int) sizeof(uint16_t) ) )
     {
        Logger_logFormatted(log, Log_WARNING, logContext,
           "prefTargetsLen(=%d) too small for given numTargets(=%d+1)",
           outFileInfo->prefTargetsLen,
           outFileInfo->numTargets);
        return -EINVAL;
     }
  
     // copy prefTargets to kernel buffer based on raw prefTargetsLen
     outFileInfo->prefTargets = memdup_user( (char __user *) userFileInfo.prefTargets,
        outFileInfo->prefTargetsLen);
     if (IS_ERR(outFileInfo->prefTargets) )
     {
        int error = PTR_ERR(outFileInfo->prefTargets);
        LOG_DEBUG_FORMATTED(log, Log_NOTICE, logContext, "Unable to copy prefTargets array");
        outFileInfo->prefTargets = NULL;
        return error;
     }
  
     // check if prefTargets given by user space has a terminating zero as last array element
     // (note: not +1, because numTargets count does not include terminating zero element)
     if(outFileInfo->prefTargets[outFileInfo->numTargets] != 0)
     {
        Logger_logFormatted(log, Log_WARNING, logContext, "prefTargets array is not zero-terminated");
        return -EINVAL;
     }
  
     return 0;
  }
  
  /**
   * Copy targets from fileInfo->prefTargets array to outCreateInfo->preferredStorageTargets list
   *
   * @param outCreateInfo outCreateInfo->preferredStorageTargets will be alloced and needs to be freed
   * by caller even in case an error is returned (if it is !=NULL)
   * @return 0 on success, negative linux error code otherwise
   */
  int IoctlHelper_ioctlCreateFileTargetsToList(App* app, struct BeegfsIoctl_MkFileV3_Arg* fileInfo,
     struct CreateInfo* outCreateInfo)
  {
     Logger* log = App_getLogger(app);
     const char* logContext = __func__;
  
     int i;
  
     // construct output list
  
     outCreateInfo->preferredStorageTargets =
        os_kmalloc(sizeof(*outCreateInfo->preferredStorageTargets));
     if(unlikely(!outCreateInfo->preferredStorageTargets) )
        return -ENOMEM;
  
     UInt16List_init(outCreateInfo->preferredStorageTargets);
  
     // copy all targets from array to list
  
     for(i=0; i < fileInfo->numTargets; i++)
     {
        uint16_t currentTarget = (fileInfo->prefTargets)[i];
  
        if(unlikely(!currentTarget) )
        { // invalid target in the middle of the array
           Logger_logFormatted(log, Log_WARNING, logContext,
              "Invalid preferred target at this array index: %d (numTargets: %d)\n",
              i, fileInfo->numTargets);
  
           return -EINVAL;
        }
  
        UInt16List_append(outCreateInfo->preferredStorageTargets, currentTarget);
     }
  
     return 0;
  }
  
  