New upstream version 8.1.0

This commit is contained in:
geos_one
2025-08-10 01:34:16 +02:00
commit c891bb7105
4398 changed files with 838833 additions and 0 deletions

View File

@@ -0,0 +1,430 @@
#include <common/storage/quota/Quota.h>
#include <common/toolkit/UnitTk.h>
#include <program/Program.h>
#include "QuotaTk.h"
#include <boost/lexical_cast.hpp>
#include <dlfcn.h>
// xfs/xfs.h includes linux/fs.h, which (sometimes) defines MS_RDONLY.
// sys/mount.h is the canonical source of MS_RDONLY, but declares it as an enum - which is then
// broken by linux/fs.h when included after it
#include <sys/mount.h>
#include <xfs/xfs.h>
#include <xfs/xqm.h>
#define QUOTATK_ZFS_USER_QUOTA "userused@"
#define QUOTATK_ZFS_GROUP_QUOTA "groupused@"
#define QUOTATK_ZFS_USER_INODE_QUOTA "userobjused@"
#define QUOTATK_ZFS_GROUP_INODE_QUOTA "groupobjused@"
/**
* Get quota data for a single ID and append it to the outQuotaDataList.
*
* @param id the user or group ID to check
* @param type the type of the ID user or group, enum QuotaDataType
* @param blockDevices the QuotaBlockDevice to check
* @param outQuotaDataList the return list with the quota data
* @param session a session for all required lib handles if zfs is used, it can be an uninitialized
* session, the initialization can be done by this function
*
* @return false on error (in which case outData is empty)
*/
bool QuotaTk::appendQuotaForID(unsigned id, QuotaDataType type, QuotaBlockDeviceMap* blockDevices,
QuotaDataList* outQuotaDataList, ZfsSession* session)
{
QuotaData data(id, type);
bool retVal = checkQuota(blockDevices, &data, session);
if(retVal && (data.getSize() != 0 || data.getInodes() != 0) )
outQuotaDataList->push_back(data);
return retVal;
}
/**
* get quota for the a range of IDs
*
* @param blockDevices the QuotaBlockDevice to check
* @param rangeStart the first ID of the ID range
* @param rangeEnd the last ID of the ID range
* @param type the quota data ID type user/group, QuotaDataType_...
* @param outQuotaDataList the return list with the quota data
* @param session a session for all required lib handles if zfs is used, it can be an uninitialized
* session, the initialization can be done by this function
*
* @return false on error (in which case outData is empty)
*/
bool QuotaTk::requestQuotaForRange(QuotaBlockDeviceMap* blockDevices, unsigned rangeStart,
unsigned rangeEnd, QuotaDataType type, QuotaDataList* outQuotaDataList, ZfsSession* session)
{
bool retVal = true;
for(unsigned id = rangeStart; id <= rangeEnd; id++)
{
bool errorVal = appendQuotaForID(id, type, blockDevices, outQuotaDataList, session);
if(!errorVal)
retVal = false;
}
return retVal;
}
/**
* get quota for the a range of IDs
*
* @param blockDevices the QuotaBlockDevice to check
* @param idList the list with the IDs to check
* @param type the quota data ID type user/group, QuotaDataType_...
* @param outQuotaDataList the return list with the quota data
* @param session a session for all required lib handles if zfs is used, it can be an uninitialized
* session, the initialization can be done by this function
*
* @return false on error (in which case outData is empty)
*/
bool QuotaTk::requestQuotaForList(QuotaBlockDeviceMap* blockDevices, UIntList* idList,
QuotaDataType type, QuotaDataList* outQuotaDataList, ZfsSession* session)
{
bool retVal = true;
for(UIntListIter iter = idList->begin(); iter != idList->end(); iter++)
{
bool errorVal = appendQuotaForID(*iter, type, blockDevices, outQuotaDataList, session);
if(!errorVal)
retVal = false;
}
return retVal;
}
/**
* get QuotaData for the given QuotaData which is initialized with type and ID
*
* @param blockDevices the QuotaBlockDevice to check
* @param outData needs to be initialized with type and ID
* @param session a session for all required lib handles if zfs is used, it can be an uninitialized
* session, the initialization can be done by this function
*
* @return false on error (in which case outData is not initialized)
*/
bool QuotaTk::checkQuota(QuotaBlockDeviceMap* blockDevices, QuotaData* outData, ZfsSession* session)
{
bool retVal = true;
int errorCode = 0;
dqblk quotaData; // required for extX
fs_disk_quota xfsQuotaData; // required for XFS
QuotaData tmpQuotaData(outData->getID(), outData->getType() ); // required for ZFS
QuotaDataType quotaType = outData->getType();
for(QuotaBlockDeviceMapIter iter = blockDevices->begin(); iter != blockDevices->end(); iter++)
{
QuotaBlockDeviceFsType fstype = iter->second.getFsType();
if(fstype == QuotaBlockDeviceFsType_XFS)
{
if(quotaType == QuotaDataType_USER)
errorCode = quotactl(QCMD(Q_XGETQUOTA, USRQUOTA),
iter->second.getBlockDevicePath().c_str(),
outData->getID(), (caddr_t)&xfsQuotaData);
else
if (quotaType == QuotaDataType_GROUP)
errorCode = quotactl(QCMD(Q_XGETQUOTA, GRPQUOTA),
iter->second.getBlockDevicePath().c_str(),
outData->getID(), (caddr_t)&xfsQuotaData);
else
{
LOG(QUOTA, ERR, "Quota request - useless quota type.",
("Type", QuotaData::QuotaDataTypeToString(outData->getType())),
("ID", outData->getID()));
return false;
}
}
else
if ( (fstype == QuotaBlockDeviceFsType_ZFS) || (fstype == QuotaBlockDeviceFsType_ZFSOLD) )
{
if(!session->isSessionValid() )
{
if(!session->initZfsSession(Program::getApp()->getDlOpenHandleLibZfs() ) )
return false;
}
if(QuotaTk::requestQuotaFromZFS(&iter->second, iter->first, &tmpQuotaData, session))
errorCode = FhgfsOpsErr_SUCCESS;
else
{
LOG(QUOTA, ERR, "Quota request - useless quota type.",
("Type", QuotaData::QuotaDataTypeToString(outData->getType())),
("ID", outData->getID()));
return false;
}
}
else
{
if(quotaType == QuotaDataType_USER)
errorCode = quotactl(QCMD(Q_GETQUOTA, USRQUOTA),
iter->second.getBlockDevicePath().c_str(),
outData->getID(), (caddr_t)&quotaData);
else
if (quotaType == QuotaDataType_GROUP)
errorCode = quotactl(QCMD(Q_GETQUOTA, GRPQUOTA),
iter->second.getBlockDevicePath().c_str(),
outData->getID(), (caddr_t)&quotaData);
else
{
LOG(QUOTA, ERR, "Quota request - useless quota type.",
("Type", QuotaData::QuotaDataTypeToString(outData->getType())),
("ID", outData->getID()));
return false;
}
}
if (errorCode != 0)
{
errorCode = errno;
// ignore error if no quota for user found (ESRCH) , especially for XFS (ENOENT)
if( (errorCode != ESRCH) &&
!((iter->second.getFsType() == QuotaBlockDeviceFsType_XFS) && (errorCode == ENOENT) ) )
{
LOG(QUOTA, ERR, "Quota request - quotactl failed.",
("Type", QuotaData::QuotaDataTypeToString(outData->getType())),
("ID", outData->getID()),
("sysErr", System::getErrString(errorCode)),
("fstype", boost::lexical_cast<std::string>(iter->second.getFsType())));
return false;
}
continue;
}
if(fstype == QuotaBlockDeviceFsType_XFS)
{
uint64_t blocks = UnitTk::quotaBlockCountToByte(xfsQuotaData.d_bcount,
iter->second.getFsType() );
outData->forceMergeQuotaDataCounter(blocks, xfsQuotaData.d_icount);
}
else
if ( (fstype == QuotaBlockDeviceFsType_ZFS) || (fstype == QuotaBlockDeviceFsType_ZFSOLD) )
{
if(tmpQuotaData.isValid() )
{
uint64_t blocks = UnitTk::quotaBlockCountToByte(tmpQuotaData.getSize(),
iter->second.getFsType() );
outData->forceMergeQuotaDataCounter(blocks, tmpQuotaData.getInodes() );
tmpQuotaData.setIsValid(false);
}
else
{
// set return value to false, but do not abort the quota request from the other targets,
// maybe the other targets returns valid values
LOG(QUOTA, ERR, "Quota request - values not valid.",
("Type", QuotaData::QuotaDataTypeToString(outData->getType())),
("ID", outData->getID()));
retVal = false;
}
}
else
{
if((quotaData.dqb_valid & QIF_USAGE) != 0)
{
uint64_t blocks = UnitTk::quotaBlockCountToByte(quotaData.dqb_curspace,
iter->second.getFsType());
outData->forceMergeQuotaDataCounter(blocks, quotaData.dqb_curinodes);
}
else
{
// set return value to false, but do not abort the quota request from the other targets,
// maybe the other targets returns valid values
LOG(QUOTA, ERR, "Quota request - values not valid.",
("Type", QuotaData::QuotaDataTypeToString(outData->getType())),
("ID", outData->getID()));
retVal = false;
}
}
}
return retVal;
}
/**
* initialize the lib zfs
*
* @param dlHandle the handle from dlopen to the libzfs
* @return the libzfs handle (libzfs_handle_t*), which is required for all libzfs calls
*/
void* QuotaTk::initLibZfs(void* dlHandle)
{
App* app = Program::getApp();
char* dlErrorString;
void* (*libzfs_init)();
libzfs_init = (void* (*)())dlsym(dlHandle, "libzfs_init");
if ( (dlErrorString = dlerror() ) != NULL)
{
LOG(QUOTA, ERR, "Error during dynamic load of function libzfs_init.", dlErrorString);
app->setLibZfsErrorReported(true);
return NULL;
}
void* libZfsHandle = (*libzfs_init)();
if(!libZfsHandle)
{
LOG(QUOTA, ERR, "Error during create of libzfs_handle_t.");
app->setLibZfsErrorReported(true);
return NULL;
}
return libZfsHandle;
}
/**
* uninitialize the lib zfs
*
* @param dlHandle the handle from dlopen to the libzfs
* @param libZfsHandle the libzfs handle (libzfs_handle_t*) to the libzfs handle (libzfs_handle_t*)
* @return false in case of error
*/
bool QuotaTk::uninitLibZfs(void* dlHandle, void* libZfsHandle)
{
App* app = Program::getApp();
if( (!dlHandle) || (!libZfsHandle) )
return true;
void (*libzfs_fini)(void*);
char* dlErrorString;
libzfs_fini = (void (*)(void*))dlsym(dlHandle, "libzfs_fini");
if ( (dlErrorString = dlerror() ) != NULL)
{
LOG(QUOTA, ERR, "Error during dynamic load of function libzfs_fini.", dlErrorString);
app->setLibZfsErrorReported(true);
return false;
}
(*libzfs_fini)(libZfsHandle);
libZfsHandle = NULL;
return true;
}
/**
* checks if all required functions of the installed libzfs are available
*
* @param blockDevice a QuotaBlockDevice for a test the get quota workflow
* @param targetNumID the targetNumID of the storage target
* @return true if all function pointers are available, false if a error occurred
*/
bool QuotaTk::checkRequiredLibZfsFunctions(QuotaBlockDevice* blockDevice, uint16_t targetNumID)
{
App* app = Program::getApp();
ZfsSession session;
// check if all function names in the lib available
if(!session.initZfsSession(app->getDlOpenHandleLibZfs() ) )
{
if(!app->getConfig()->getQuotaDisableZfsSupport() )
LOG(QUOTA, ERR, "Cannot initialize libzfs. "
"The quota system for all zfs blockdevices/pools on this machine won't work.");
return false;
}
// check if the signature of all function pointers are compatible
QuotaData quotaData(0, QuotaDataType_USER); // test request for user root
if (!requestQuotaFromZFS(blockDevice, targetNumID, &quotaData, &session))
{ // quota update failed; try to get quota without inode quota (only supported on zfs >= 0.7.4)
// i.e. set the fsType to QuotaBlockDeviceFsType_ZFSOLD and try again
// if it still doesn't work, libzfs is not compatible at all, if it works now we fall back to
// quota without inode quota
blockDevice->setFsType(QuotaBlockDeviceFsType_ZFSOLD);
if (!requestQuotaFromZFS(blockDevice, targetNumID, &quotaData, &session))
return false;
}
// check function pointers which are required in case of errors happens
std::string errorDec( (*session.libzfs_error_description)(session.getlibZfsHandle() ) );
std::string errorAct( (*session.libzfs_error_action)(session.getlibZfsHandle() ) );
return true;
}
/**
* get QuotaData from zfs for the given QuotaData which is initialized with type and ID
*
* @param blockDevice the QuotaBlockDevice to check
* @param targetNumID the targetNumID of the storage target
* @param outData needs to be initialized with type and ID
* @param session a session for all required lib handles if zfs is used, it can be an uninitialized
* session, the initialization can be done by this function
* @return false on error (in which case outData is not initialized)
*/
bool QuotaTk::requestQuotaFromZFS(QuotaBlockDevice* blockDevice, uint16_t targetNumID,
QuotaData* outData, ZfsSession* session)
{
if(!session->isSessionValid() )
{
outData->setIsValid(false);
return false;
}
void* zfsHandle = session->getZfsDeviceHandle(targetNumID, blockDevice->getBlockDevicePath() );
if(!zfsHandle)
return false;
uint64_t usedSizeValue = 0;
uint64_t usedInodesValue = 0;
std::string sizeProp;
std::string inodeProp;
if(outData->getType() == QuotaDataType_USER)
{
sizeProp = QUOTATK_ZFS_USER_QUOTA + StringTk::uintToStr(outData->getID());
inodeProp = QUOTATK_ZFS_USER_INODE_QUOTA + StringTk::uintToStr(outData->getID());
}
else
{
sizeProp = QUOTATK_ZFS_GROUP_QUOTA + StringTk::uintToStr(outData->getID());
inodeProp = QUOTATK_ZFS_GROUP_INODE_QUOTA + StringTk::uintToStr(outData->getID());
}
if ((*session->zfs_prop_get_userquota_int)(zfsHandle, sizeProp.c_str(), &usedSizeValue))
{
LOG(QUOTA, ERR, "Error during request quota data.",
("ErrorAction", (*session->libzfs_error_action)(session->getlibZfsHandle())),
("ErrorDescription", (*session->libzfs_error_description)(session->getlibZfsHandle()))
);
return false;
}
if (blockDevice->getFsType() != QuotaBlockDeviceFsType_ZFSOLD) // no inode support on zfs<=0.7.4
{
if ((*session->zfs_prop_get_userquota_int)(zfsHandle, inodeProp.c_str(), &usedInodesValue))
{
// could not read inode quota (which is only supported since zfs 0.7.4)
// log the error and set 0 as inode quota
LOG(QUOTA, ERR, "Inode quota could not be requested. Please note that inode quota on"
" ZFS is not supported for zfs versions prior to 0.7.4.",
("ZFS Error", (*session->libzfs_error_description)(session->getlibZfsHandle())));
return false;
}
}
outData->setQuotaData(usedSizeValue, usedInodesValue);
return true;
}

View File

@@ -0,0 +1,43 @@
#pragma once
#include <common/Common.h>
#include <common/storage/quota/QuotaData.h>
#include <session/ZfsSession.h>
#include <storage/QuotaBlockDevice.h>
class QuotaTk
{
public:
/**
* functions for extX and XFS support
*/
static bool appendQuotaForID(unsigned id, QuotaDataType type,
QuotaBlockDeviceMap* blockDevices, QuotaDataList* outQuotaDataList, ZfsSession* session);
static bool requestQuotaForRange(QuotaBlockDeviceMap* blockDevices, unsigned rangeStart,
unsigned rangeEnd, QuotaDataType type, QuotaDataList* outQuotaDataList,
ZfsSession* session);
static bool requestQuotaForList(QuotaBlockDeviceMap* blockDevices, UIntList* idList,
QuotaDataType type, QuotaDataList* outQuotaDataList, ZfsSession* session);
static bool checkQuota(QuotaBlockDeviceMap* blockDevices, QuotaData* outData,
ZfsSession* session);
/**
* functions for ZFS support
*/
static void* initLibZfs(void* dlHandle);
static bool uninitLibZfs(void* dlHandle, void* libZfsHandle);
static bool checkRequiredLibZfsFunctions(QuotaBlockDevice* blockDevice, uint16_t targetNumID);
static bool requestQuotaFromZFS(QuotaBlockDevice* blockDevice, uint16_t targetNumID,
QuotaData* outData, ZfsSession* session);
private:
QuotaTk();
};

View File

@@ -0,0 +1,64 @@
#pragma once
#include <app/config/Config.h>
#include <common/Common.h>
#include <common/fsck/FsckChunk.h>
#include <common/storage/Path.h>
#include <common/storage/Storagedata.h>
#include <common/storage/StorageErrors.h>
#include <common/threading/Mutex.h>
#include <common/toolkit/StorageTk.h>
#include <common/threading/SafeRWLock.h>
#include <dirent.h>
#define STORAGETK_FORMAT_MIN_VERSION 2
#define STORAGETK_FORMAT_CURRENT_VERSION 3
#define MAX_SERIAL_ENTRYINFO_SIZE 4096
#define STORAGETK_DEFAULTCHUNKDIRMODE (S_IRWXU | S_IRWXG | S_IRWXO)
#define STORAGETK_DEFAULTCHUNKFILEMODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
class StorageTkEx
{
private:
StorageTkEx() {}
public:
static bool getDynamicFileAttribs(const int fd, int64_t* outFilesize,
int64_t* outAllocedBlocks, int64_t* outModificationTimeSecs,
int64_t* outLastAccessTimeSecs)
{
struct stat statbuf;
int statRes = fstat(fd, &statbuf);
if (statRes)
return false;
*outFilesize = statbuf.st_size;
*outAllocedBlocks = statbuf.st_blocks;
*outModificationTimeSecs = statbuf.st_mtime;
*outLastAccessTimeSecs = statbuf.st_atime;
return true;
}
static bool getDynamicFileAttribs(const int dirFD, const char* path, int64_t* outFilesize,
int64_t* outAllocedBlocks, int64_t* outModificationTimeSecs,
int64_t* outLastAccessTimeSecs)
{
struct stat statbuf;
int statRes = fstatat(dirFD, path, &statbuf, 0);
if (statRes)
return false;
*outFilesize = statbuf.st_size;
*outAllocedBlocks = statbuf.st_blocks;
*outModificationTimeSecs = statbuf.st_mtime;
*outLastAccessTimeSecs = statbuf.st_atime;
return true;
}
};