on this file might be covered by the GNU General Public License.
*/
/***************************************************************************
- escape.cpp - escaping of strings
+ filefunc.cpp - functions for working with FS
-------------------
begin : Sun Nov 14 1999
copyright : (C) 1999 by Intra2net AG
#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/statvfs.h>
#include <dirent.h>
#include <pwd.h>
#include <grp.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
+#include <map>
+#include <set>
#include <boost/scoped_array.hpp>
#include <boost/foreach.hpp>
Mtime = stat_info.st_mtime;
Ctime = stat_info.st_atime;
+ // the stat(2) manpage for linux defines that st_blocks is given in a number of 512-byte-blocks.
+ BytesOnDisk = stat_info.st_blocks;
+ BytesOnDisk*=(long long)512;
+
IsLink= S_ISLNK( stat_info.st_mode );
IsRegular= S_ISREG( stat_info.st_mode );
IsDirectory= S_ISDIR( stat_info.st_mode );
Gid = 0;
DeviceType = 0;
Size = 0;
+ BytesOnDisk = 0;
Atime = 0;
Mtime = 0;
Ctime = 0;
std::vector< std::string >& result,
bool include_dot_names )
{
+ // code copied to get_dir_count; keep in sync
DIR* dir = ::opendir( path.c_str());
if (!dir)
{
} // eo get_dir(const std::string&,bool)
+/**
+ * @brief count entries in directory, like get_dir(path, include_dot_names).size()
+ * @param path the path to the directory whose contents should be read.
+ * @param include_dot_names determines if dot-files should be included in the count.
+ * @return the number of entries in the directory; return -1 in case of error
+ */
+int get_dir_count(const std::string& path, bool include_dot_names)
+{
+ // code is a simplified copy of get_dir, above. Keep in sync
+ int result = 0;
+ DIR* dir = ::opendir( path.c_str());
+ if (!dir)
+ return -1;
+ struct dirent store, *entry = NULL;
+ while (readdir_r(dir, &store, &entry) == 0 && entry != NULL)
+ {
+ if (entry->d_name == NULL)
+ continue; // should not happen
+ else if (! include_dot_names && (entry->d_name)[0] == '.')
+ continue;
+ ++result;
+ }
+ ::closedir(dir);
+ return result;
+}
+
/**
* @brief removes a file from a filesystem.
{
bool rtn = true;
- try {
- struct stat my_stat;
- if (stat(path.c_str(), &my_stat) != 0) {
+ try
+ {
+ Stat sp(path, false);
+ if (!sp)
throw runtime_error("can't stat " + path);
- }
- if (S_ISDIR(my_stat.st_mode)) {
- DIR *dir = opendir(path.c_str());
- if (!dir) {
- throw runtime_error("can't open directory " + path);
- }
-
- struct dirent store, *entry = NULL;
- while (readdir_r(dir, &store, &entry) == 0 && entry != NULL)
+ if (sp.is_directory())
+ {
+ std::vector<std::string> dirents = get_dir(path, false);
+ BOOST_FOREACH(const std::string &filename, dirents)
{
- string filename = entry->d_name;
- if (filename == "." || filename == "..") {
- continue;
- }
-
// Delete subdir or file.
rtn = recursive_delete(path + "/" + filename, false, error);
- if (rtn == false) {
+ if (rtn == false)
break;
- }
}
- closedir(dir);
- if (keep_parent_dir == false && !rmdir(path)) {
+ if (keep_parent_dir == false && !rmdir(path))
throw runtime_error("can't remove directory " + path);
- }
- } else {
- if (!unlink(path)) {
+ }
+ else
+ {
+ if (!unlink(path))
throw runtime_error("can't unlink " + path);
- }
}
- } catch (exception &e) {
- if (error) {
+ }
+ catch (exception &e)
+ {
+ if (error)
+ {
ostringstream out;
out << e.what() << " (" << strerror(errno) << ")";
*error = out.str();
}
rtn = false;
- } catch (...) {
- if (error) {
+ }
+ catch (...)
+ {
+ if (error)
+ {
ostringstream out;
out << "unknown error (" << strerror(errno) << ")";
*error = out.str();
/**
Create a unique temporary directory from path_template.
@param Path template. The last six characters must be XXXXXX.
- @param error Will contain the error if the return value is false [optional]
+ @param error Will contain the error if the return value is empty [optional]
@return Name of new directory or empty string on error.
+
+ @seealso: classes in tmpfstream which offer functionality based on mkstemp
*/
std::string mkdtemp(const std::string &path_template, std::string *error)
{
/**
* @brief Remove unlisted files
*
- * @param directory Direcotry to look for files
+ * @param directory Directory to look for files
* @param keep_files List of files or directories to keep
* @param prefix Filename prefix to match. Empty prefix matches all.
*
return all_fine;
}
+/**
+ * @brief Get free size in bytes on a given path or filename
+ *
+ * @param path Directory or filename to look in
+ *
+ * @return Number of bytes available to a regular user, -1 in case of an error
+ **/
+long long get_free_diskspace(const std::string& path)
+{
+ struct statvfs sf;
+
+ int looplimit=10000;
+ int ret;
+ while ( ((ret=statvfs(path.c_str(),&sf)) == -1) && (errno==EINTR) && looplimit > 0)
+ looplimit--;
+
+ if (ret==-1)
+ {
+ // a real error occured
+ return -1;
+ }
+
+ long long free_bytes=0;
+
+ // load block size
+ free_bytes=sf.f_bsize;
+
+ // multiply by number of free blocks accessible by normal users
+ // make sure we really multiply long long by long long and don't overflow at 2 GB
+ free_bytes*=(long long)sf.f_bavail;
+
+ return free_bytes;
+}
+
+namespace
+{
+// anonymous namespace to make du_internal inaccessible from outside
+
+// internally used by du, do not use for other things
+void du_internal(const std::string &path, long long &sum, std::map<dev_t, std::set<ino_t> > &counted_inodes)
+{
+
+ Stat sp(path, false); // don't dereference symlinks here
+ if (!sp)
+ throw runtime_error("can't stat " + path);
+
+ // make sure we don't count hardlinked files twice
+ bool count_file=true;
+
+ // dirs can't be hardlinked, their nlink is the size of entries -> doesn't matter for us here
+ if (!sp.is_directory() && sp.nlink() > 1)
+ {
+ // see if we have remembered this dev / inode combination
+ if (counted_inodes[sp.device()].count(sp.inode()))
+ count_file=false;
+ else
+ counted_inodes[sp.device()].insert(sp.inode());
+ }
+
+ // always add the space used, even if we have a directory, symlink or whatever:
+ // they need space on disk too
+
+ if (count_file)
+ sum+=sp.bytes_on_disk();
+
+ if (sp.is_directory())
+ {
+ std::vector<std::string> dirents = get_dir(path, false);
+ BOOST_FOREACH(const std::string &filename, dirents)
+ {
+ // calculate size of subdir or file
+ du_internal(path + "/" + filename, sum, counted_inodes);
+ }
+ }
+}
+
+} // eo anon namespace
+
+/**
+ * like du(1): return the number bytes used by a directory structure, counting hardlinked files only once
+ * @param path File or directory to start counting recursively
+ * @param error Will contain the error if the return value is -1 [optional]
+ * @return size in bytes on success, -1 on error
+ */
+long long du(const std::string &path, std::string *error)
+{
+ long long sum = 0;
+
+ std::map<dev_t, std::set<ino_t> > counted_inodes;
+
+ try
+ {
+ du_internal(path, sum, counted_inodes);
+ }
+ catch (exception &e)
+ {
+ if (error)
+ {
+ ostringstream out;
+ out << e.what() << " (" << strerror(errno) << ")";
+ *error = out.str();
+ }
+ return -1;
+ }
+ catch (...)
+ {
+ if (error)
+ {
+ ostringstream out;
+ out << "unknown error (" << strerror(errno) << ")";
+ *error = out.str();
+ }
+ return -1;
+ }
+
+ return sum;
+} // eo du
+
} // eo namespace I2n