Mercurial > hg > release > heapstats-1.0
view agent/src/fsUtil.cpp @ 38:2d49f99d0a36
Bug 1735: REGRESSION: Cannot collect files from procfs
reviewed-by: ykubota
author | Yasumasa Suenaga <yasuenag@gmail.com> |
---|---|
date | Mon, 14 Apr 2014 22:23:05 +0900 |
parents | 48dc7c3ceee5 |
children | c80ffa6f3759 |
line wrap: on
line source
/*! * \file fsUtil.cpp * \brief This file is utilities to access file system. * Copyright (C) 2011-2014 Nippon Telegraph and Telephone Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <dirent.h> #include <fstream> #include <unistd.h> #include <errno.h> #include <fcntl.h> #include <sys/sendfile.h> #include "fsUtil.hpp" #include "util.hpp" /*! * \brief Mutex of working directory. */ pthread_mutex_t directoryMutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP; /*! * \brief Copy data as avoid overwriting. * \param sourceFile [in] Path of source file. * \param destPath [in] Path of directory put duplicated file. * \param destName [in] Name of duplicated file.<br> * Don't rename, if value is null. * \return Prosess is succeed or failure. */ bool copyFile(char const* sourceFile, char const* destPath, char const* destName){ char rpath[PATH_MAX]; if(unlikely(!isCopiablePath(sourceFile, rpath))){ return false; } /* Make file name. */ char *newFile = destName == NULL ? createFilename(destPath, (char*)sourceFile) : createFilename(destPath, (char*)destName); /* Failure make filename. */ if(unlikely(newFile == NULL)){ PRINT_WARN_MSG("Couldn't allocate copy source file path."); return false; } /* Get uniq file name. */ char *destFile = createUniquePath(newFile, false); if(unlikely(destFile == NULL)){ PRINT_WARN_MSG("Couldn't allocate unique destination file name."); free(newFile); return false; } /* Open copy source file. */ int sourceFd = open(sourceFile, O_RDONLY); if(unlikely(sourceFd < 0)){ PRINT_WARN_MSG("Couldn't open copy source file."); free(newFile); free(destFile); return false; } /* Get source file size */ struct stat st; if(unlikely(fstat(sourceFd, &st) != 0)){ PRINT_WARN_MSG("Couldn't open copy destination file."); free(newFile); free(destFile); close(sourceFd); return false; } /* Open destination file. */ int destFd = open(destFile, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR); if (unlikely(destFd < 0)) { PRINT_WARN_MSG("Couldn't open copy destination file."); free(newFile); free(destFile); close(sourceFd); return false; } /* Copy data */ if(st.st_size > 0){ if(unlikely(sendfile64(destFd, sourceFd, NULL, st.st_size) == -1)){ PRINT_WARN_MSG("Couldn't copy file."); } } else{ /* This route is for files in procfs */ char buf[1024]; ssize_t read_size; while((read_size = read(sourceFd, buf, 1024)) > 0){ if(write(destFd, buf, (size_t)read_size) == -1){ read_size = -1; break; } } if(read_size == -1){ PRINT_WARN_MSG("Couldn't copy file."); } } /* Cleanup. */ close(sourceFd); close(destFd); free(newFile); free(destFile); return true; } /*! * \brief Create filename in expected path. * \param basePath [in] Path of directory. * \param filename [in] filename. * \return Create string.<br> * Process is failure, if return is null.<br> * Need call "free", if return is not null. */ char *createFilename(char const* basePath, char const* filename) { /* Path separator. */ const char PATH_SEP = '/'; /* Sanity check. */ if (unlikely(basePath == NULL || filename == NULL || strlen(basePath) == 0 || strlen(filename) == 0)) { return NULL; } /* Find file name head. so exclude path. */ char *headPos = rindex((char*)filename, PATH_SEP); /* If not found separator. */ if (headPos == NULL) { /* Copy all. */ headPos = (char*)filename; } else { /* Skip separator. */ headPos++; } /* Check separator existing. */ bool needSeparator = (basePath[strlen(basePath) - 1] != PATH_SEP); size_t strSize = strlen(basePath) + strlen(headPos) + 1; if (needSeparator) { strSize += 1; } char *newPath = (char*)malloc(strSize); /* If failure allocate result string. */ if (unlikely(newPath == NULL)) { return NULL; } /* Add base directory path. */ strcpy(newPath, basePath); /* Insert separator. */ if (needSeparator) { strcat(newPath, "/"); } /* Add file name. */ strcat(newPath, headPos); /* Return generate file path. */ return newPath; } /*! * \brief Resolve canonicalized path path and check regular file or not. * \param path [in] Path of the target file. * \param rpath [out] Real path which is pointed at "path". * Buffer size must be enough to store path (we recommend PATH_MAX). * \return If "path" is copiable, this function returns true. */ bool isCopiablePath(const char *path, char *rpath){ realpath(path, rpath); struct stat st; if(unlikely(stat(rpath, &st) != 0)){ /* Failure get file information. */ PRINT_WARN_MSG("Failure get file information (stat)."); return false; } if((st.st_mode & S_IFMT) != S_IFREG){ /* File isn't regular file. This route is not error. */ return false; } /* File is able copy. */ return true; } /*! * \brief Create temporary directory in current directory. * \param basePath [out] Path of temporary directory.<br> * Process is failure, if return is null.<br> * Need call "free", if return is not null. * \param wishesName [in] Name of one's wishes directory name. */ void createTempDir(char **basePath, char const* wishesName) { char *uniqName = NULL; /* Sanity check. */ if (unlikely(basePath == NULL || wishesName == NULL)) { PRINT_WARN_MSG("Illegal directory path."); return; } *basePath = NULL; /* Get mutex. */ ENTER_PTHREAD_SECTION(&directoryMutex) /* Create unique directory path. */ uniqName = createUniquePath((char*)wishesName, true); if (unlikely(uniqName == NULL)) { PRINT_WARN_MSG("Couldn't create unique directory path."); pthread_mutex_unlock(&directoryMutex); return; } /* Create temporary directory in current directory. */ if (unlikely(mkdir(uniqName, S_IRUSR | S_IWUSR | S_IXUSR) != 0)) { PRINT_WARN_MSG("Couldn't create temporary directory."); pthread_mutex_unlock(&directoryMutex); free(uniqName); return; } /* Release mutex. */ EXIT_PTHREAD_SECTION(&directoryMutex) if (likely(uniqName != NULL)) { /* Copy directory path. */ *basePath = strdup(uniqName); /* Cleanup. */ free(uniqName); } /* If failure copy directory path. */ if (unlikely(*basePath == NULL)) { PRINT_WARN_MSG("Couldn't copy temporary directory path."); } } /*! * \brief Remove temporary directory. * \param basePath [in] Path of temporary directory. */ void removeTempDir(char const* basePath) { /* If failure open directory. */ if (unlikely(basePath == NULL)) { PRINT_WARN_MSG("Illegal directory path."); return; } /* Open directory. */ DIR *dir = opendir(basePath); /* If failure open directory. */ if (unlikely(dir == NULL)) { PRINT_WARN_MSG("Couldn't open directory."); return; } /* Reset error number. */ errno = 0; /* Read file in directory. */ struct dirent *entry = NULL; while ((entry = readdir(dir)) != NULL) { /* Check file name. */ if (strcmp(entry->d_name, "..") == 0 || strcmp(entry->d_name, ".") == 0) { continue; } /* Get remove file's full path. */ char *removePath = createFilename(basePath, entry->d_name); /* All file need remove. */ if (unlikely(removePath == NULL)){ PRINT_WARN_MSG_HEADER << "Failure remove file." << " path:\"" << entry->d_name << "\"" << NEWLINE; continue; } if (unlikely(unlink(removePath) != 0)) { /* Skip file remove. */ PRINT_WARN_MSG_HEADER << "Failure remove file." << " path:\"" << removePath << "\"" << NEWLINE; } /* Cleanup. */ free(removePath); } /* If failure in read directory. */ if (unlikely(errno != 0)) { PRINT_WARN_MSG("Failure search file in directory."); } /* Cleanup. */ closedir(dir); /* Get mutex. */ ENTER_PTHREAD_SECTION(&directoryMutex) /* Remove directory. */ if (unlikely(rmdir(basePath) != 0)) { /* Skip directory remove. */ PRINT_WARN_MSG("Failure remove directory."); } /* Release mutex. */ EXIT_PTHREAD_SECTION(&directoryMutex) } /*! * \brief Create unique path. * \param path [in] Path. * \param isDirectory [in] Path is directory. * \return Unique path.<br>Don't forget deallocate. */ char *createUniquePath(char *path, bool isDirectory) { /* Variable for temporary path. */ char tempPath[PATH_MAX + 1] = {0}; /* Variables for file system. */ FILE *file = NULL; DIR *dir = NULL; /* Variable for file path and extensition. */ char ext[PATH_MAX + 1] = {0}; char tempName[PATH_MAX + 1] = {0}; /* Sanity check. */ if (unlikely(path == NULL || strlen(path) == 0)) { PRINT_WARN_MSG("Illegal unique path paramters."); return NULL; } /* Search extension. */ char *extPos = strrchr(path, '.'); /* If create path for file and exists extension. */ if (!isDirectory && extPos != NULL) { int pathSize = (extPos - path); pathSize = (pathSize > PATH_MAX) ? PATH_MAX : pathSize; /* Path and extension store each other. */ strncpy(ext, extPos, PATH_MAX); strncpy(tempName, path, pathSize); } else { /* Ignore extension if exists. */ strncpy(tempName, path, PATH_MAX); } /* Copy default path. */ strncpy(tempPath, path, PATH_MAX); /* Try make unique path loop. */ const unsigned long int MAX_RETRY_COUNT = 1000000; unsigned long int loopCount = 0; for (; loopCount <= MAX_RETRY_COUNT; loopCount++) { /* Usable path flag. */ bool noExist = false; /* Reset error number. */ errno = 0; /* Open and check exists. */ if (isDirectory) { /* Check directory. */ dir = opendir(tempPath); noExist = (dir == NULL && errno == ENOENT); if (dir != NULL) { closedir(dir); } } else { /* Check file. */ file = fopen(tempPath, "r"); noExist = (file == NULL && errno == ENOENT); if (file != NULL) { fclose(file); } } /* If path is usable. */ if (noExist) { break; } /* Make new path insert sequence number between 000000 and 999999. */ snprintf(tempPath, PATH_MAX, "%s_%06lu%s", tempName, loopCount, ext); } /* If give up to try unique naming by number. */ if (unlikely(loopCount > MAX_RETRY_COUNT)) { /* Get random value. */ long int rand = random(); const int randSize = 6; char randStr[randSize + 1] = {0}; for (int i = 0; i < randSize; i++) { randStr[i] = (rand % 26) + 'A'; rand /= 26; } /* Uniquely naming by random string. */ snprintf(tempPath, PATH_MAX, "%s_%6s%s", tempName, randStr, ext); PRINT_WARN_MSG("Not found unique name. So used random string."); } /* Copy path. */ char *result = strdup(tempPath); if (unlikely(result == NULL)) { PRINT_WARN_MSG("Couldn't copy unique path."); } return result; } /*! * \brief Get parent directory path. * \param path [in] Path which file or directory. * \return Parent directory path.<br>Don't forget deallocate. */ char *getParentDirectoryPath(char const* path) { char *sepPos = NULL; char *result = NULL; /* Search last separator. */ sepPos = strrchr((char*)path, '/'); if (sepPos == NULL) { /* Path is current directory. */ result = strdup("./"); } else if (unlikely(sepPos == path)) { /* Path is root. */ result = strdup("/"); } else { /* Copy path exclude last entry. */ result = (char*)calloc(1, sepPos - path + 1); if (likely(result != NULL)) { strncpy(result, path, sepPos - path); } } /* If failure allocate path. */ if (unlikely(result == NULL)) { PRINT_WARN_MSG("Couldn't copy parent directory path."); } return result; } /*! * \brief Get accessible of directory. * \param path [in] Directory path. * \param needRead [in] Accessible flag about file read. * \param needWrite [in] Accessible flag about file write. * \return This value is true, if directory path is accessible.<br> * This value is false, if process is failure or path is inaccessible. */ bool isAccessibleDirectory(char const* path, bool needRead, bool needWrite) { struct stat st = {0}; /* Sanity check. */ if (unlikely(path == NULL || strlen(path) == 0 || (!needRead && !needWrite))) { PRINT_WARN_MSG("Illegal accessible paramter."); return false; } /* If failure get directory information. */ if (unlikely(stat(path, &st) != 0)) { PRINT_WARN_MSG("Failure get directory information."); return false; } /* if path isn't directory. */ if (unlikely(!S_ISDIR(st.st_mode))) { PRINT_WARN_MSG("Illegal directory path."); return false; } /* if execute by administrator. */ if (unlikely(geteuid() == 0)) { return true; } bool result = false; bool isDirOwner = (st.st_uid == geteuid()); bool isGroupUser = (st.st_gid == getegid()); /* Check access permition as file owner. */ if (isDirOwner) { result |= (!needRead || (st.st_mode & S_IRUSR) > 0) && (!needWrite || (st.st_mode & S_IWUSR) > 0); } /* Check access permition as group user. */ if (!result && isGroupUser) { result |= (!needRead || (st.st_mode & S_IRGRP) > 0) && (!needWrite || (st.st_mode & S_IWGRP) > 0); } /* Check access permition as other. */ if (!result) { result |= (!needRead || (st.st_mode & S_IROTH) > 0) && (!needWrite || (st.st_mode & S_IWOTH) > 0); } return result; }