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 <unistd.h>
#include <errno.h>
#include <string.h>
+#include <map>
+#include <set>
#include <boost/scoped_array.hpp>
#include <boost/foreach.hpp>
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.
/**
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)
{
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
+ * 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
- *
- * @attention This function does currently not consider that hardlinked files need
- * space on disk only once. But this may be added later if needed. So make
- * sure that calling functions don't depend on either behavior!
*/
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
{
- Stat sp(path, false); // don't dereference symlinks here
- if (!sp)
- throw runtime_error("can't stat " + path);
-
- // always add the space used, even if we have a directory, symlink or whatever:
- // they need space on disk too
-
- 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
- long long rtn = du(path + "/" + filename, error);
- if (rtn == -1)
- return -1;
- sum+=rtn;
- }
- }
+ du_internal(path, sum, counted_inodes);
}
catch (exception &e)
{