swad-core/swad_browser_size.c

490 lines
18 KiB
C

// swad_browser_size.c: file browser size
/*
SWAD (Shared Workspace At a Distance),
is a web platform developed at the University of Granada (Spain),
and used to support university teaching.
This file is part of SWAD core.
Copyright (C) 1999-2024 Antonio Cañas Vargas
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*****************************************************************************/
/*********************************** Headers *********************************/
/*****************************************************************************/
#include <dirent.h> // For scandir, etc.
#include <stdlib.h> // For free
#include <string.h> // For string functions
#include <sys/stat.h> // For lstat
#include "swad_alert.h"
#include "swad_browser_database.h"
#include "swad_browser_size.h"
#include "swad_database.h"
#include "swad_error.h"
#include "swad_global.h"
/*****************************************************************************/
/**************************** Private constants ******************************/
/*****************************************************************************/
/* All quotas must be multiple of 1 GiB (Gibibyte)*/
#define BrwSiz_GiB (1024ULL * 1024ULL * 1024ULL)
/* Maximum quotas for each type of file browser */
#define BrwSiz_MAX_QUOTA_DOCUM_INS (64ULL*BrwSiz_GiB)
#define BrwSiz_MAX_FILES_DOCUM_INS 5000
#define BrwSiz_MAX_FOLDS_DOCUM_INS 1000
#define BrwSiz_MAX_QUOTA_DOCUM_CTR (64ULL*BrwSiz_GiB)
#define BrwSiz_MAX_FILES_DOCUM_CTR 5000
#define BrwSiz_MAX_FOLDS_DOCUM_CTR 1000
#define BrwSiz_MAX_QUOTA_DOCUM_DEG (64ULL*BrwSiz_GiB)
#define BrwSiz_MAX_FILES_DOCUM_DEG 5000
#define BrwSiz_MAX_FOLDS_DOCUM_DEG 1000
#define BrwSiz_MAX_QUOTA_DOCUM_CRS (64ULL*BrwSiz_GiB)
#define BrwSiz_MAX_FILES_DOCUM_CRS 5000
#define BrwSiz_MAX_FOLDS_DOCUM_CRS 1000
#define BrwSiz_MAX_QUOTA_DOCUM_GRP ( 1ULL*BrwSiz_GiB)
#define BrwSiz_MAX_FILES_DOCUM_GRP 1000
#define BrwSiz_MAX_FOLDS_DOCUM_GRP 500
#define BrwSiz_MAX_QUOTA_TEACH_CRS BrwSiz_MAX_QUOTA_DOCUM_CRS
#define BrwSiz_MAX_FILES_TEACH_CRS BrwSiz_MAX_FILES_DOCUM_CRS
#define BrwSiz_MAX_FOLDS_TEACH_CRS BrwSiz_MAX_FOLDS_DOCUM_CRS
#define BrwSiz_MAX_QUOTA_TEACH_GRP BrwSiz_MAX_QUOTA_DOCUM_GRP
#define BrwSiz_MAX_FILES_TEACH_GRP BrwSiz_MAX_FILES_DOCUM_GRP
#define BrwSiz_MAX_FOLDS_TEACH_GRP BrwSiz_MAX_FOLDS_DOCUM_GRP
#define BrwSiz_MAX_QUOTA_SHARE_INS (64ULL*BrwSiz_GiB)
#define BrwSiz_MAX_FILES_SHARE_INS 5000
#define BrwSiz_MAX_FOLDS_SHARE_INS 1000 // Many, because every student can create his own directories
#define BrwSiz_MAX_QUOTA_SHARE_CTR (64ULL*BrwSiz_GiB)
#define BrwSiz_MAX_FILES_SHARE_CTR 5000
#define BrwSiz_MAX_FOLDS_SHARE_CTR 1000 // Many, because every student can create his own directories
#define BrwSiz_MAX_QUOTA_SHARE_DEG (64ULL*BrwSiz_GiB)
#define BrwSiz_MAX_FILES_SHARE_DEG 5000
#define BrwSiz_MAX_FOLDS_SHARE_DEG 1000 // Many, because every student can create his own directories
#define BrwSiz_MAX_QUOTA_SHARE_CRS (64ULL*BrwSiz_GiB)
#define BrwSiz_MAX_FILES_SHARE_CRS 5000
#define BrwSiz_MAX_FOLDS_SHARE_CRS 1000 // Many, because every student can create his own directories
#define BrwSiz_MAX_QUOTA_SHARE_GRP ( 1ULL*BrwSiz_GiB)
#define BrwSiz_MAX_FILES_SHARE_GRP 1000
#define BrwSiz_MAX_FOLDS_SHARE_GRP 500 // Many, because every student can create his own directories
#define BrwSiz_MAX_QUOTA_ASSIG_PER_STD ( 2ULL*BrwSiz_GiB)
#define BrwSiz_MAX_FILES_ASSIG_PER_STD 500
#define BrwSiz_MAX_FOLDS_ASSIG_PER_STD 50
#define BrwSiz_MAX_QUOTA_WORKS_PER_STD ( 2ULL*BrwSiz_GiB)
#define BrwSiz_MAX_FILES_WORKS_PER_STD 500
#define BrwSiz_MAX_FOLDS_WORKS_PER_STD 50
#define BrwSiz_MAX_QUOTA_DOC_PRJ ( 1ULL*BrwSiz_GiB)
#define BrwSiz_MAX_FILES_DOC_PRJ 500
#define BrwSiz_MAX_FOLDS_DOC_PRJ 50
#define BrwSiz_MAX_QUOTA_ASS_PRJ ( 1ULL*BrwSiz_GiB)
#define BrwSiz_MAX_FILES_ASS_PRJ 200
#define BrwSiz_MAX_FOLDS_ASS_PRJ 20
#define BrwSiz_MAX_QUOTA_MARKS_CRS ( 1ULL*BrwSiz_GiB)
#define BrwSiz_MAX_FILES_MARKS_CRS 500
#define BrwSiz_MAX_FOLDS_MARKS_CRS 50
#define BrwSiz_MAX_QUOTA_MARKS_GRP ( 1ULL*BrwSiz_GiB)
#define BrwSiz_MAX_FILES_MARKS_GRP 200
#define BrwSiz_MAX_FOLDS_MARKS_GRP 20
static unsigned long long BrwSiz_MAX_QUOTA_BRIEF[Rol_NUM_ROLES] = // MaxRole is used
{
[Rol_STD] = 32ULL*BrwSiz_GiB,
[Rol_NET] = 32ULL*BrwSiz_GiB,
[Rol_TCH] = 64ULL*BrwSiz_GiB,
};
#define BrwSiz_MAX_FILES_BRIEF 5000
#define BrwSiz_MAX_FOLDS_BRIEF 1000
/*****************************************************************************/
/************** External global variables from others modules ****************/
/*****************************************************************************/
extern struct Globals Gbl;
/*****************************************************************************/
/************************* Private global variables **************************/
/*****************************************************************************/
struct BrwSiz_BrowserSize Brw_Size;
/*****************************************************************************/
/***************************** Private prototypes ****************************/
/*****************************************************************************/
static void BrwSiz_CalcSizeOfDirRecursive (struct BrwSiz_BrowserSize *Size,
unsigned Level,char *Path);
/*****************************************************************************/
/********************* Get pointer to browser size struct ********************/
/*****************************************************************************/
struct BrwSiz_BrowserSize *BrwSiz_GetSize (void)
{
return &Brw_Size;
}
/*****************************************************************************/
/*** Initialize maximum quota of current file browser and check if exceded ***/
/*****************************************************************************/
void BrwSiz_SetAndCheckQuota (struct BrwSiz_BrowserSize *Size)
{
extern const char *Txt_Quota_exceeded;
/***** Check the quota *****/
BrwSiz_SetMaxQuota (Size);
BrwSiz_CalcSizeOfDir (Size,Gbl.FileBrowser.Path.RootFolder);
if (BrwSiz_CheckIfQuotaExceded (Size))
Ale_ShowAlert (Ale_WARNING,Txt_Quota_exceeded);
}
/*****************************************************************************/
/************ Initialize maximum quota of current file browser ***************/
/*****************************************************************************/
void BrwSiz_SetMaxQuota (struct BrwSiz_BrowserSize *Size)
{
switch (Gbl.FileBrowser.Type)
{
case Brw_SHOW_DOC_INS:
case Brw_ADMI_DOC_INS:
Size->MaxQuota = BrwSiz_MAX_QUOTA_DOCUM_INS;
Size->MaxFiles = BrwSiz_MAX_FILES_DOCUM_INS;
Size->MaxFolds = BrwSiz_MAX_FOLDS_DOCUM_INS;
break;
case Brw_ADMI_SHR_INS:
Size->MaxQuota = BrwSiz_MAX_QUOTA_SHARE_INS;
Size->MaxFiles = BrwSiz_MAX_FILES_SHARE_INS;
Size->MaxFolds = BrwSiz_MAX_FOLDS_SHARE_INS;
break;
case Brw_SHOW_DOC_CTR:
case Brw_ADMI_DOC_CTR:
Size->MaxQuota = BrwSiz_MAX_QUOTA_DOCUM_CTR;
Size->MaxFiles = BrwSiz_MAX_FILES_DOCUM_CTR;
Size->MaxFolds = BrwSiz_MAX_FOLDS_DOCUM_CTR;
break;
case Brw_ADMI_SHR_CTR:
Size->MaxQuota = BrwSiz_MAX_QUOTA_SHARE_CTR;
Size->MaxFiles = BrwSiz_MAX_FILES_SHARE_CTR;
Size->MaxFolds = BrwSiz_MAX_FOLDS_SHARE_CTR;
break;
case Brw_SHOW_DOC_DEG:
case Brw_ADMI_DOC_DEG:
Size->MaxQuota = BrwSiz_MAX_QUOTA_DOCUM_DEG;
Size->MaxFiles = BrwSiz_MAX_FILES_DOCUM_DEG;
Size->MaxFolds = BrwSiz_MAX_FOLDS_DOCUM_DEG;
break;
case Brw_ADMI_SHR_DEG:
Size->MaxQuota = BrwSiz_MAX_QUOTA_SHARE_DEG;
Size->MaxFiles = BrwSiz_MAX_FILES_SHARE_DEG;
Size->MaxFolds = BrwSiz_MAX_FOLDS_SHARE_DEG;
break;
case Brw_SHOW_DOC_CRS:
case Brw_ADMI_DOC_CRS:
Size->MaxQuota = BrwSiz_MAX_QUOTA_DOCUM_CRS;
Size->MaxFiles = BrwSiz_MAX_FILES_DOCUM_CRS;
Size->MaxFolds = BrwSiz_MAX_FOLDS_DOCUM_CRS;
break;
case Brw_SHOW_DOC_GRP:
case Brw_ADMI_DOC_GRP:
Size->MaxQuota = BrwSiz_MAX_QUOTA_DOCUM_GRP;
Size->MaxFiles = BrwSiz_MAX_FILES_DOCUM_GRP;
Size->MaxFolds = BrwSiz_MAX_FOLDS_DOCUM_GRP;
break;
case Brw_ADMI_TCH_CRS:
Size->MaxQuota = BrwSiz_MAX_QUOTA_TEACH_CRS;
Size->MaxFiles = BrwSiz_MAX_FILES_TEACH_CRS;
Size->MaxFolds = BrwSiz_MAX_FOLDS_TEACH_CRS;
break;
case Brw_ADMI_TCH_GRP:
Size->MaxQuota = BrwSiz_MAX_QUOTA_TEACH_GRP;
Size->MaxFiles = BrwSiz_MAX_FILES_TEACH_GRP;
Size->MaxFolds = BrwSiz_MAX_FOLDS_TEACH_GRP;
break;
case Brw_ADMI_SHR_CRS:
Size->MaxQuota = BrwSiz_MAX_QUOTA_SHARE_CRS;
Size->MaxFiles = BrwSiz_MAX_FILES_SHARE_CRS;
Size->MaxFolds = BrwSiz_MAX_FOLDS_SHARE_CRS;
break;
case Brw_ADMI_SHR_GRP:
Size->MaxQuota = BrwSiz_MAX_QUOTA_SHARE_GRP;
Size->MaxFiles = BrwSiz_MAX_FILES_SHARE_GRP;
Size->MaxFolds = BrwSiz_MAX_FOLDS_SHARE_GRP;
break;
case Brw_ADMI_ASG_USR:
case Brw_ADMI_ASG_CRS:
Size->MaxQuota = BrwSiz_MAX_QUOTA_ASSIG_PER_STD;
Size->MaxFiles = BrwSiz_MAX_FILES_ASSIG_PER_STD;
Size->MaxFolds = BrwSiz_MAX_FOLDS_ASSIG_PER_STD;
break;
case Brw_ADMI_WRK_USR:
case Brw_ADMI_WRK_CRS:
Size->MaxQuota = BrwSiz_MAX_QUOTA_WORKS_PER_STD;
Size->MaxFiles = BrwSiz_MAX_FILES_WORKS_PER_STD;
Size->MaxFolds = BrwSiz_MAX_FOLDS_WORKS_PER_STD;
break;
case Brw_ADMI_DOC_PRJ:
Size->MaxQuota = BrwSiz_MAX_QUOTA_DOC_PRJ;
Size->MaxFiles = BrwSiz_MAX_FILES_DOC_PRJ;
Size->MaxFolds = BrwSiz_MAX_FOLDS_DOC_PRJ;
break;
case Brw_ADMI_ASS_PRJ:
Size->MaxQuota = BrwSiz_MAX_QUOTA_ASS_PRJ;
Size->MaxFiles = BrwSiz_MAX_FILES_ASS_PRJ;
Size->MaxFolds = BrwSiz_MAX_FOLDS_ASS_PRJ;
break;
case Brw_SHOW_MRK_CRS:
case Brw_ADMI_MRK_CRS:
Size->MaxQuota = BrwSiz_MAX_QUOTA_MARKS_CRS;
Size->MaxFiles = BrwSiz_MAX_FILES_MARKS_CRS;
Size->MaxFolds = BrwSiz_MAX_FOLDS_MARKS_CRS;
break;
case Brw_SHOW_MRK_GRP:
case Brw_ADMI_MRK_GRP:
Size->MaxQuota = BrwSiz_MAX_QUOTA_MARKS_GRP;
Size->MaxFiles = BrwSiz_MAX_FILES_MARKS_GRP;
Size->MaxFolds = BrwSiz_MAX_FOLDS_MARKS_GRP;
break;
case Brw_ADMI_BRF_USR:
Size->MaxQuota = BrwSiz_MAX_QUOTA_BRIEF[Gbl.Usrs.Me.Role.Max];
Size->MaxFiles = BrwSiz_MAX_FILES_BRIEF;
Size->MaxFolds = BrwSiz_MAX_FOLDS_BRIEF;
break;
default:
break;
}
}
/*****************************************************************************/
/********************** Check if quota has been exceeded *********************/
/*****************************************************************************/
bool BrwSiz_CheckIfQuotaExceded (const struct BrwSiz_BrowserSize *Size)
{
return (Size->NumLevls > BrwSiz_MAX_DIR_LEVELS ||
Size->NumFolds > Size->MaxFolds ||
Size->NumFiles > Size->MaxFiles ||
Size->TotalSiz > Size->MaxQuota);
}
/*****************************************************************************/
/********************* Reset the size of a file browser **********************/
/*****************************************************************************/
void BrwSiz_ResetFileBrowserSize (struct BrwSiz_BrowserSize *Size)
{
Size->NumLevls = 0;
Size->NumFolds =
Size->NumFiles = 0L;
Size->TotalSiz = 0ULL;
}
/*****************************************************************************/
/********************** Compute the size of a directory **********************/
/*****************************************************************************/
void BrwSiz_CalcSizeOfDir (struct BrwSiz_BrowserSize *Size,char *Path)
{
BrwSiz_ResetFileBrowserSize (Size);
BrwSiz_CalcSizeOfDirRecursive (Size,1,Path);
}
/*****************************************************************************/
/**************** Compute the size of a directory recursively ****************/
/*****************************************************************************/
static void BrwSiz_CalcSizeOfDirRecursive (struct BrwSiz_BrowserSize *Size,
unsigned Level,char *Path)
{
struct dirent **FileList;
int NumFile;
int NumFiles;
char PathFileRel[PATH_MAX + 1];
struct stat FileStatus;
/***** Scan the directory *****/
if ((NumFiles = scandir (Path,&FileList,NULL,NULL)) >= 0) // No error
{
/***** Compute recursively the total number and size of the files and folders *****/
for (NumFile = 0;
NumFile < NumFiles;
NumFile++)
{
if (strcmp (FileList[NumFile]->d_name,".") &&
strcmp (FileList[NumFile]->d_name,"..")) // Skip directories "." and ".."
{
/* There are files in this directory ==> update level */
if (Level > Size->NumLevls)
Size->NumLevls++;
/* Update counters depending on whether it's a directory or a regular file */
snprintf (PathFileRel,sizeof (PathFileRel),"%s/%s",
Path,FileList[NumFile]->d_name);
if (lstat (PathFileRel,&FileStatus)) // On success ==> 0 is returned
Err_ShowErrorAndExit ("Can not get information about a file or folder.");
else if (S_ISDIR (FileStatus.st_mode)) // It's a directory
{
Size->NumFolds++;
Size->TotalSiz += (unsigned long long) FileStatus.st_size;
BrwSiz_CalcSizeOfDirRecursive (Size,Level + 1,PathFileRel);
}
else if (S_ISREG (FileStatus.st_mode)) // It's a regular file
{
Size->NumFiles++;
Size->TotalSiz += (unsigned long long) FileStatus.st_size;
}
}
free (FileList[NumFile]);
}
free (FileList);
}
else
Err_ShowErrorAndExit ("Error while scanning directory.");
}
/*****************************************************************************/
/**************** Get the size of a file zone from database ******************/
/*****************************************************************************/
void BrwSiz_GetSizeOfFileZone (Brw_FileBrowser_t FileBrowser,
struct BrwSiz_SizeOfFileZone *SizeOfFileZone)
{
MYSQL_RES *mysql_res;
MYSQL_ROW row;
/***** Get the size of a file browser *****/
/* Query database */
Brw_DB_GetSizeOfFileBrowser (&mysql_res,FileBrowser);
/* Get row */
row = mysql_fetch_row (mysql_res);
/* Reset default values to zero */
SizeOfFileZone->NumCrss =
SizeOfFileZone->NumUsrs = 0;
SizeOfFileZone->MaxLevels = 0;
SizeOfFileZone->NumFolders =
SizeOfFileZone->NumFiles = 0;
SizeOfFileZone->Size = 0;
/* Get number of courses (row[0]) */
if (row[0])
if (sscanf (row[0],"%d",&(SizeOfFileZone->NumCrss)) != 1)
Err_ShowErrorAndExit ("Error when getting number of courses.");
/* Get number of groups (row[1]) */
if (row[1])
if (sscanf (row[1],"%d",&(SizeOfFileZone->NumGrps)) != 1)
Err_ShowErrorAndExit ("Error when getting number of groups.");
/* Get number of users (row[2]) */
if (row[2])
if (sscanf (row[2],"%d",&(SizeOfFileZone->NumUsrs)) != 1)
Err_ShowErrorAndExit ("Error when getting number of users.");
/* Get maximum number of levels (row[3]) */
if (row[3])
if (sscanf (row[3],"%u",&(SizeOfFileZone->MaxLevels)) != 1)
Err_ShowErrorAndExit ("Error when getting maximum number of levels.");
/* Get number of folders (row[4]) */
if (row[4])
if (sscanf (row[4],"%lu",&(SizeOfFileZone->NumFolders)) != 1)
Err_ShowErrorAndExit ("Error when getting number of folders.");
/* Get number of files (row[5]) */
if (row[5])
if (sscanf (row[5],"%lu",&(SizeOfFileZone->NumFiles)) != 1)
Err_ShowErrorAndExit ("Error when getting number of files.");
/* Get total size (row[6]) */
if (row[6])
if (sscanf (row[6],"%llu",&(SizeOfFileZone->Size)) != 1)
Err_ShowErrorAndExit ("Error when getting toal size.");
/* Free structure that stores the query result */
DB_FreeMySQLResult (&mysql_res);
}
/*****************************************************************************/
/************************* Show size of a file browser ***********************/
/*****************************************************************************/
void BrwSiz_ShowAndStoreSizeOfFileBrowser (const struct BrwSiz_BrowserSize *Size)
{
extern const char *Txt_level;
extern const char *Txt_levels;
extern const char *Txt_folder;
extern const char *Txt_folders;
extern const char *Txt_file;
extern const char *Txt_files;
extern const char *Txt_of_PART_OF_A_TOTAL;
char FileSizeStr[Fil_MAX_BYTES_FILE_SIZE_STRING + 1];
HTM_DIV_Begin ("class=\"CM DAT_%s\"",The_GetSuffix ());
if (Brw_CheckIfFileBrowserIsEditable (Gbl.FileBrowser.Type))
{
Fil_WriteFileSizeFull ((double) Size->TotalSiz,FileSizeStr);
HTM_TxtF ("%u %s; %lu %s; %lu %s",
Size->NumLevls,
Size->NumLevls == 1 ? Txt_level :
Txt_levels ,
Size->NumFolds,
Size->NumFolds == 1 ? Txt_folder :
Txt_folders,
Size->NumFiles,
Size->NumFiles == 1 ? Txt_file :
Txt_files);
HTM_BR ();
HTM_Txt (FileSizeStr);
if (Size->MaxQuota)
{
Fil_WriteFileSizeBrief ((double) Size->MaxQuota,FileSizeStr);
HTM_TxtF (" (%.1f%% %s %s)",
100.0 * ((double) Size->TotalSiz /
(double) Size->MaxQuota),
Txt_of_PART_OF_A_TOTAL,
FileSizeStr);
}
Brw_DB_StoreSizeOfFileBrowser (Size);
}
// else
// HTM_NBSP (); // Blank to occupy the same space as the text for the browser size
HTM_DIV_End ();
}