// 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 . */ /*****************************************************************************/ /*********************************** Headers *********************************/ /*****************************************************************************/ #include // For scandir, etc. #include // For free #include // For string functions #include // 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 (); }