Files
interactive/fhs/sh-opendav/disk.c
T
2026-08-07 17:38:18 +09:00

282 lines
9.3 KiB
C

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <paths.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <err.h>
#include <sys/mman.h>
#include <sys/time.h>
#ifdef __FreeBSD__
#include <sys/resource.h>
#include <devstat.h>
#include <sys/devicestat.h>
#include <sys/param.h>
#include <sys/ucred.h>
#include <sys/mount.h>
#else // linux
#include <mntent.h>
#include <sys/vfs.h>
#endif // __FreeBSD__
#include <sys/types.h>
#include <sys/stat.h>
#include <httpd.h>
#include <http_config.h>
#include <http_protocol.h>
#include <http_log.h>
#include "dav_repos.h"
#define DISABLE_FLAG ".disable"
// CHG 2019-09-24 huibong Bigbox FHS 장비 사용에 따른 mount 별 운영여유공간 계산방식 개선 (#32807)
// - 285 TB 급 bigbox FHS 장비 사용으로 인해
// - 기존과 같이 각 mount 별 1% 여유공간을 남길경우... 2.8 TB 가 남겨져 저장공간 낭비가 심해
// - 다음과 같이 개선 처리함.
// -- 각 mount total 공간 ( used + avail ) > 3TB 이면... avail 공간은 30 GByte 초과할 경우 write 가능
// -- 각 mount total 공간 ( used + avail ) <= 3TB 이면... 기존과 같이 사용률이 99% 미만인 경우 write 가능
// - 기존 conf 상의 DavProviderStorageLimit 설정은 제거 처리
#define MOUNT_WRITE_CHECK_PERCENT_LIMIT 3298534883328 // 3 TB
#define MOUNT_WRITE_LIMIT_SIZE 32212254720 // 30 GB
#define MOUNT_WRITE_LIMIT_PERCENT 99 // 99%
typedef struct
{
char * f_mntonname;
apr_int64_t f_bavail; // 해당 mount 의 여유용량, 8Byte long 최대값은 9,223,372,036,854,775,807 이므로.. 9,223 PB 까지 저장 가능.
apr_int64_t total_size; // 해당 mount 의 total size, UFS 의 8% 예약공간 이슈로... used size + avail size 로 계산한다. (2019-09-24 #32807 로 추가)
int capacity; // 사용량 정보 (%), int 형변환 때문에 99.99 % -> 99 로 표시됨.
} diskinfo;
int get_writable_path(apr_pool_t *p, const char *homepath, char *subpath)
{
// 2015.06.15 dadamin
// 램덤 처리 시 동일한 타입을 사용않을 시
// 특정 볼륨(node0)로 몰리는 현상이 발견되어
// 램덤 함수에서 사용하는 타입으로 수정함
unsigned int current = 0;
diskinfo *info;
apr_array_header_t *storgelist = NULL;
long used, availblks;
#ifdef __FreeBSD__
int mntsize = 0, i = 0;
struct statfs *mntbuf = NULL;
apr_global_mutex_lock(local_disk_lock);
mntsize = getmntinfo(&mntbuf, MNT_WAIT);
if( mntsize == 0 )
{
apr_global_mutex_unlock(local_disk_lock);
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "get_writable_path::getmntinfo error [%d]", errno);
return -1; // error
}
storgelist = apr_array_make(p, 0, sizeof(diskinfo));
if(storgelist == NULL)
{
apr_global_mutex_unlock(local_disk_lock);
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "get_writable_path::apr_array_make error [%d]", errno);
return -1; // error
}
for (i = 0; i < mntsize; i++)
{
// storage root
if (strncmp(homepath, mntbuf[i].f_mntonname, strlen(homepath)))
continue;
info = (diskinfo *) apr_array_push(storgelist);
// multi volume
info->f_mntonname = mntbuf[i].f_mntonname;
info->f_bavail = mntbuf[i].f_bavail * mntbuf[i].f_bsize;
used = mntbuf[i].f_blocks - mntbuf[i].f_bfree;
availblks = mntbuf[i].f_bavail + used;
// 각 mount total size 정보는 UFS 예약 8% 를 제거하기 위해 실제 사용량 + free 용량 정보를 더해서 계산한다.
info->total_size = availblks * mntbuf[i].f_bsize;
info->capacity = (int)(availblks == 0 ? 100.0 : (double)used / (double)availblks * 100.0);
}
apr_global_mutex_unlock(local_disk_lock);
#else // linux
FILE *f;
struct mntent mnt;
struct mntent *mnte;
char buf[512];
if ((f = setmntent ("/etc/mtab", "r")) != NULL)
{
storgelist = apr_array_make(p, 0, sizeof(diskinfo));
while ((mnte = getmntent_r (f, &mnt, buf, sizeof(buf))) != NULL)
{
// storage root
if (strncmp(homepath, mnt.mnt_dir, strlen(homepath)))
continue;
struct statfs statfsbuf;
if(statfs(mnt.mnt_dir, &statfsbuf) == 0)
{
info = (diskinfo *) apr_array_push(storgelist);
// multi volume
info->f_mntonname = mnt.mnt_dir;
info->f_bavail = statfsbuf.f_bavail * statfsbuf.f_bsize;
used = statfsbuf.f_blocks - statfsbuf.f_bfree;
availblks = statfsbuf.f_bavail + used;
// 각 mount total size 정보는 특정 시스템의 예약 8% 를 제거하기 위해 실제 사용량 + free 용량 정보를 더해서 계산한다.
info->total_size = availblks * statfsbuf.f_bsize;
info->capacity = (int)(availblks == 0 ? 100.0 : (double)used / (double)availblks * 100.0);
}
else
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "get_writable_path:: multi volume statfs error [%d]", errno);
}
endmntent(f);
}
else
{
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "get_writable_path::setmntent error [%d]", errno);
return -1; // error
}
#endif //
if(storgelist->nelts == 0)
{
// single volume
struct statfs statfsbuf;
if(statfs(homepath, &statfsbuf) == 0)
{
info = (diskinfo *) apr_array_push(storgelist);
info->f_mntonname = (char *)homepath;
info->f_bavail = statfsbuf.f_bavail * statfsbuf.f_bsize;
used = statfsbuf.f_blocks - statfsbuf.f_bfree;
availblks = statfsbuf.f_bavail + used;
// 각 mount total size 정보는 UFS 예약 8% 를 제거하기 위해 실제 사용량 + free 용량 정보를 더해서 계산한다.
info->total_size = availblks * statfsbuf.f_bsize;
info->capacity = (int)(availblks == 0 ? 100.0 : (double)used / (double)availblks * 100.0);
}
else
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "get_writable_path::statfs error [%d]", errno);
}
if(storgelist->nelts > 0)
{
char disable_flag[1024] = {0};
struct stat sb;
int not_exist = -1;
apr_generate_random_bytes((unsigned char *)&current, sizeof(unsigned int));
current = current % storgelist->nelts;
info = &((diskinfo *) storgelist->elts)[current];
sprintf(disable_flag, "%s%s", info->f_mntonname, DISABLE_FLAG);
not_exist = stat(disable_flag, &sb);
// CHG 2019-09-24 huibong Bigbox FHS 장비 사용에 따른 mount 별 운영여유공간 계산방식 개선 (#32807)
// - 285 TB 급 bigbox FHS 장비 사용으로 인해
// - 기존과 같이 각 mount 별 1% 여유공간을 남길경우... 2.8 TB 가 남겨져 저장공간 낭비가 심해
// - 다음과 같이 개선 처리함.
// -- 각 mount total 공간 ( used + avail ) > 3TB 이면... avail 공간은 30 GByte 초과할 경우 write 가능
// -- 각 mount total 공간 ( used + avail ) <= 3TB 이면... 기존과 같이 사용률이 99% 미만인 경우 write 가능
// - 기존 conf 상의 DavProviderStorageLimit 설정은 제거 처리
if( info->total_size > MOUNT_WRITE_CHECK_PERCENT_LIMIT )
{
if( not_exist && info->f_bavail > MOUNT_WRITE_LIMIT_SIZE )
{
strcpy( subpath, info->f_mntonname );
// for debug
//ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "get_writable_path: random, total size greater 3TB, free size greater 30GB, return [%s]", subpath );
return 0;
}
}
else
{
if( not_exist && info->capacity < MOUNT_WRITE_LIMIT_PERCENT )
{
strcpy( subpath, info->f_mntonname );
// for debug
//ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "get_writable_path: random, total size less than or equal 3TB, used percent less than 99%%, return [%s]", subpath );
return 0;
}
}
}
// 2014-02-28 dadamin
// 램덤 실패 시 해당 결과 순회 후 정상 디스크를 선택
int first = 0, diskcnt = storgelist->nelts;
first = current;
for ( current = first ; current < diskcnt; ++current)
{
char disable_flag[1024] = {0};
struct stat sb;
int not_exist = -1;
info = &((diskinfo *) storgelist->elts)[current];
sprintf(disable_flag, "%s%s", info->f_mntonname, DISABLE_FLAG);
not_exist = stat(disable_flag, &sb);
// CHG 2019-09-24 huibong Bigbox FHS 장비 사용에 따른 mount 별 운영여유공간 계산방식 개선 (#32807)
// - 285 TB 급 bigbox FHS 장비 사용으로 인해
// - 기존과 같이 각 mount 별 1% 여유공간을 남길경우... 2.8 TB 가 남겨져 저장공간 낭비가 심해
// - 다음과 같이 개선 처리함.
// -- 각 mount total 공간 ( used + avail ) > 3TB 이면... avail 공간은 30 GByte 초과할 경우 write 가능
// -- 각 mount total 공간 ( used + avail ) <= 3TB 이면... 기존과 같이 사용률이 99% 미만인 경우 write 가능
// - 기존 conf 상의 DavProviderStorageLimit 설정은 제거 처리
if( info->total_size > MOUNT_WRITE_CHECK_PERCENT_LIMIT )
{
if( not_exist && info->f_bavail > MOUNT_WRITE_LIMIT_SIZE )
{
strcpy( subpath, info->f_mntonname );
// for debug
//ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "get_writable_path: sequence, total size greater 3TB, free size greater 30GB, return [%s]", subpath );
return 0;
}
}
else
{
if( not_exist && info->capacity < MOUNT_WRITE_LIMIT_PERCENT )
{
strcpy( subpath, info->f_mntonname );
// for debug
//ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "get_writable_path: sequence, total size less than or equal 3TB, used percent less than 99%%, return [%s]", subpath );
return 0;
}
}
if( first > 0 && (current+1) >= diskcnt )
{
current = -1;
diskcnt = first;
first = 0;
}
}
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "get_writable_path::Empty writable");
return -1; // error
}