add Stat::BytesOnDisk()
[libi2ncommon] / src / filefunc.cpp
1 /*
2 The software in this package is distributed under the GNU General
3 Public License version 2 (with a special exception described below).
4
5 A copy of GNU General Public License (GPL) is included in this distribution,
6 in the file COPYING.GPL.
7
8 As a special exception, if other files instantiate templates or use macros
9 or inline functions from this file, or you compile this file and link it
10 with other works to produce a work based on this file, this file
11 does not by itself cause the resulting work to be covered
12 by the GNU General Public License.
13
14 However the source code for this file must still be made available
15 in accordance with section (3) of the GNU General Public License.
16
17 This exception does not invalidate any other reasons why a work based
18 on this file might be covered by the GNU General Public License.
19 */
20 /***************************************************************************
21                           escape.cpp  -  escaping of strings
22                              -------------------
23     begin                : Sun Nov 14 1999
24     copyright            : (C) 1999 by Intra2net AG
25  ***************************************************************************/
26
27 #include <list>
28 #include <string>
29 #include <fstream>
30 #include <sstream>
31 #include <iostream>
32 #include <stdexcept>
33
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <sys/statvfs.h>
37 #include <dirent.h>
38 #include <pwd.h>
39 #include <grp.h>
40 #include <unistd.h>
41 #include <errno.h>
42 #include <string.h>
43
44 #include <boost/scoped_array.hpp>
45 #include <boost/foreach.hpp>
46 #include "filefunc.hxx"
47 #include "stringfunc.hxx"
48
49 namespace I2n
50 {
51
52 using namespace std;
53
54 /*
55 ** implementation of Stat
56 */
57
58 Stat::Stat()
59     : FollowLinks(true)
60     , Valid(false)
61 {
62    clear();
63 } // eo Stat::Stat()
64
65
66 Stat::Stat(const std::string& path, bool follow_links)
67 {
68     stat(path,follow_links);
69 } // eo Stat::Stat(const std::string&,bool)
70
71
72 Stat::~Stat()
73 {
74 } // eo Stat::~Stat()
75
76
77 /**
78  * @brief updates the internal data.
79  *
80  * In other words: stat()'s the file again.
81  */
82 void Stat::recheck()
83 {
84     if (! Path.empty())
85     {
86         // pass a copy of Path: otherwise clear() would leave an empty reference
87         stat(string(Path), FollowLinks);
88     }
89 } // eo Stat::recheck()
90
91
92 /**
93  * @brief calls stat() or lstat() to get the information for the given path
94  *  and stores that information.
95  * @param path the path which should be checked
96  * @param follow_links determine if (symbalic) links should be followed.
97  */
98 void Stat::stat(const std::string& path, bool follow_links)
99 {
100     clear();
101     Path= path;
102     FollowLinks= follow_links;
103     struct stat stat_info;
104     int res;
105     res = ( follow_links ? ::stat(path.c_str(), &stat_info) : ::lstat(path.c_str(), &stat_info) );
106     if ( 0 == res )
107     {
108         Device = stat_info.st_dev;
109         Inode  = stat_info.st_ino;
110         Mode   = (stat_info.st_mode & ~(S_IFMT));
111         NumLinks = stat_info.st_nlink;
112         Uid    = stat_info.st_uid;
113         Gid    = stat_info.st_gid;
114         DeviceType = stat_info.st_rdev;
115         Size   = stat_info.st_size;
116         Atime  = stat_info.st_atime;
117         Mtime  = stat_info.st_mtime;
118         Ctime  = stat_info.st_atime;
119
120         // the stat(2) manpage for linux defines that st_blocks is given in a number of 512-byte-blocks.
121         BytesOnDisk = stat_info.st_blocks;
122         BytesOnDisk*=(long long)512;
123
124         IsLink=          S_ISLNK( stat_info.st_mode );
125         IsRegular=       S_ISREG( stat_info.st_mode );
126         IsDirectory=     S_ISDIR( stat_info.st_mode );
127         IsCharacterDevice= S_ISCHR( stat_info.st_mode );
128         IsBlockDevice=   S_ISBLK( stat_info.st_mode );
129         IsFifo=          S_ISFIFO( stat_info.st_mode );
130         IsSocket=        S_ISSOCK( stat_info.st_mode );
131     }
132     Valid = (0 == res);
133 } // eo Stat::stat(const std::string&,bool)
134
135
136 /**
137  * @brief clears the internal data.
138  */
139 void Stat::clear()
140 {
141     Path.clear();
142     Valid= false;
143
144     Device = 0;
145     Inode  = 0;
146     Mode   = 0;
147     NumLinks = 0;
148     Uid    = 0;
149     Gid    = 0;
150     DeviceType = 0;
151     Size   = 0;
152     BytesOnDisk = 0;
153     Atime  = 0;
154     Mtime  = 0;
155     Ctime  = 0;
156
157     IsLink= false;
158     IsRegular= false;
159     IsDirectory= false;
160     IsCharacterDevice= false;
161     IsBlockDevice= false;
162     IsFifo= false;
163     IsSocket= false;
164 } // eo Stat::clear()
165
166
167 /**
168  * @brief checks if another instance describes the same file.
169  * @param rhs the other instance.
170  * @return @a true iff the other instance describes the same file.
171  * @note
172  * The "same file" means that the files are located on the same device and use the same inode.
173  * They might still have two different directory entries (different paths)!
174  */
175 bool Stat::is_same_as(const Stat& rhs)
176 {
177     return Valid and rhs.Valid
178         and ( Device == rhs.Device)
179         and ( Inode == rhs.Inode);
180 } // eo Stat::is_same_as(const Stat& rhs);
181
182
183 /**
184  * @brief checks if this and the other instance describe the same device.
185  * @param rhs the other instance.
186  * @return @a true if we and the other instance describe a device and the same device.
187  *
188  * "Same device" means that the devices have the same type and the same major and minor id.
189  */
190 bool Stat::is_same_device_as(const Stat& rhs)
191 {
192     return is_device() and rhs.is_device()
193         and ( IsBlockDevice == rhs.IsBlockDevice )
194         and ( IsCharacterDevice == rhs.IsCharacterDevice )
195         and ( DeviceType == rhs.DeviceType);
196 } // eo Stat::is_same_device_as(const Stat&)
197
198 /**
199  * @brief check existence of a path.
200  * @param path path which should be tested.
201  * @return @a true iff path exists.
202  */
203 bool path_exists(const std::string& path)
204 {
205     struct stat stat_info;
206     int res = ::stat(path.c_str(), &stat_info);
207     if (res) return false;
208     return true;
209 } // eo path_exists(const std::string&)
210
211 /**
212  * @brief check existence of a regular file.
213  * @param path path which should be tested.
214  * @return @a true if path exists and is a regular file (or a link pointing to a regular file).
215  * @note this checks for regular files; not for the pure exitsnace of a path; use pathExists() for that.
216  * @see pathExists
217  */
218 bool file_exists(const std::string& path)
219 {
220     struct stat stat_info;
221     int res = ::stat(path.c_str(), &stat_info);
222     if (res) return false;
223     return S_ISREG(stat_info.st_mode);
224 } // eo file_exists(const std::string&)
225
226 // TODO: Use Stat class
227 /**
228  * Get size of file
229  * @param name filename to get size for
230  * @return file size or -1 if file does not exist
231  */
232 long file_size (const string &name)
233 {
234     long iReturn = -1;
235
236     struct stat statbuff;
237
238     if (lstat(name.c_str(), &statbuff) < 0)
239         return -1;
240
241     if (!S_ISREG(statbuff.st_mode))
242         return -1;
243
244     iReturn=statbuff.st_size;
245
246     return iReturn;
247 }
248
249 /**
250  * @brief tests the last modification time stamp of a path.
251  * @param path path which should be tested.
252  * @return the last modification time or 0 if the path doen't exist.
253  */
254 time_t file_mtime(const std::string& path)
255 {
256     struct stat stat_info;
257     int res = ::stat(path.c_str(), &stat_info);
258     if (res) return 0;
259     return stat_info.st_mtime;
260 } // eo file_mtime(const std::string&)
261
262
263 /**
264  * @brief Check if two files differ
265  *
266  *        Note: Reads the whole file into memory
267  *              if the file size is identical.
268  *
269  * @param old_filename Filename of old file
270  * @param new_filename Filename of new file
271  *
272  * @return bool True if files differ, false otherwise.
273  *              If one file does not exist, also returns true
274  */
275 bool file_content_differs(const std::string &old_filename, const std::string &new_filename)
276 {
277     if (I2n::file_exists(old_filename) == false ||
278         I2n::file_exists(new_filename) == false)
279         return true;
280
281     // check if size differs
282     if (I2n::file_size(old_filename) != I2n::file_size(new_filename))
283         return true;
284
285     const std::string old_content = I2n::read_file(old_filename);
286     const std::string new_content = I2n::read_file(new_filename);
287
288     // check if content differs
289     if (old_content == new_content)
290         return false;
291
292     // Differ by default (fallback)
293     return true;
294 }
295
296
297 /**
298  * @brief reads the contents of a directory.
299  * @param path the path to the directory whose contents should be read.
300  * @param[out] result the resulting list of names.
301  * @param include_dot_names determines if dot-files should be included in the list.
302  * @return @a true if reading the directory was succesful, @a false on error.
303  */
304 bool get_dir(
305     const std::string& path,
306     std::vector< std::string >& result,
307     bool include_dot_names )
308 {
309     DIR* dir = ::opendir( path.c_str());
310     if (!dir)
311     {
312         return false;
313     }
314     struct dirent store, *entry = NULL;
315     while (readdir_r(dir, &store, &entry) == 0 && entry != NULL)
316     {
317         std::string name( entry->d_name );
318         if (! include_dot_names && (name[0] == '.') )
319         {
320             continue;
321         }
322         result.push_back( name );
323     }
324     ::closedir(dir);
325     return true;
326 } // eo get_dir(const std::string&,std::vector< std::string >&,bool)
327
328
329 /**
330  * @brief reads the contents of a directory
331  * @param path the path to the directory whose contents should be read.
332  * @param include_dot_names determines if dot-files should be included in the list.
333  * @return the list of names (empty on error).
334  */
335 std::vector< std::string > get_dir(const std::string& path, bool include_dot_names )
336 {
337     std::vector< std::string > result;
338     get_dir(path,result,include_dot_names);
339     return result;
340 } // eo get_dir(const std::string&,bool)
341
342
343
344 /**
345  * @brief removes a file from a filesystem.
346  * @param path path to the file.
347  * @return @a true if the unlink was successful.
348  */
349 bool unlink( const std::string& path )
350 {
351     int res = ::unlink( path.c_str() );
352     return (res == 0);
353 } // eo unlink(const std::string&)
354
355
356
357 /**
358  * @brief creates a symbolic link named @a link_name to @a target.
359  * @param target the target the link should point to.
360  * @param link_name the name of the link.
361  * @param force if @a true, the (file or link) @a link_name is removed if it exists.
362  * @return @a true iff the symlink was successfully created.
363  */
364 bool symlink(const std::string& target, const std::string& link_name, bool force)
365 {
366     int res= -1;
367     if (target.empty() or link_name.empty() or target == link_name)
368     {
369         // no, we don't do this!
370         return false;
371     }
372     std::string n_target;
373     if (target[0] == '/') // absolute target?
374     {
375         n_target= target;
376     }
377     else // relative target
378     {
379         // for stat'ing: prepend dir of link_name:
380         n_target= dirname(link_name)+"/"+ target;
381     }
382     Stat target_stat(n_target, false);
383     Stat link_name_stat(link_name, false);
384     if (target_stat.exists() && link_name_stat.exists())
385     {
386         if (link_name_stat.is_same_as(target_stat)
387             or link_name_stat.is_same_device_as(target_stat) )
388         {
389             return false;
390         }
391         //TODO: more consistency checks?!
392     }
393     if (link_name_stat.exists())
394     {
395         // the link name already exists.
396         if (force)
397         {
398             // "force" as given, so try to remove the link_name:
399             unlink(link_name);
400             // update the stat:
401             link_name_stat.recheck();
402         }
403     }
404     if (link_name_stat.exists())
405     {
406         // well, if the link_name still exists; we cannot create that link:
407         errno = EEXIST;
408         return false;
409     }
410     res= ::symlink(target.c_str(), link_name.c_str());
411     return (0 == res);
412 } // eo symlink(const std::string&,const std::string&,bool)
413
414
415
416 /**
417  * @brief reads the target of a symbolic link.
418  * @param path path to the symbolic link
419  * @return the target of the link or an empty string on error.
420  */
421 std::string read_link(const std::string& path)
422 {
423     errno= 0;
424     Stat stat(path,false);
425     if (!stat || !stat.is_link())
426     {
427         return std::string();
428     }
429     int buffer_size= PATH_MAX+1 + 128;
430     boost::scoped_array<char> buffer_ptr( new char[buffer_size] );
431     int res= ::readlink( path.c_str(), buffer_ptr.get(), buffer_size-1 );
432     if (res >= 0)
433     {
434         return std::string( buffer_ptr.get(), res );
435     }
436     return std::string();
437 } // eo read_link(const std::string&)
438
439
440
441 /**
442  * @brief returns content of a file as string.
443  *
444  * A simple (q'n'd) function for retrieving content of a file as string.<br>
445  * Also able to read special (but regular) files which don't provide a size when stat'ed
446  * (like files in the /proc filesystem).
447  *
448  * @param path path to the file.
449  * @return the content of the file as string (empty if file could be opened).
450  */
451 std::string read_file(const std::string& path)
452 {
453     Stat stat(path);
454     if (!stat.is_reg())
455     {
456         return std::string();
457     }
458     std::ifstream f( path.c_str(), std::ios::in | std::ios::binary );
459     std::string result;
460     if (f.is_open())
461     {
462         // NOTE: there are cases where we don't know the correct size (/proc files...)
463         // therefore we only use the size for reserving space if we know it, but don't
464         // use it when reading the file!
465         if (stat.size() > 0)
466         {
467             // if we know the size, we reserve enough space.
468             result.reserve( stat.size() );
469         }
470         char buffer[2048];
471         while (f.good())
472         {
473             f.read(buffer, sizeof(buffer));
474             result.append(buffer, f.gcount());
475         }
476     }
477     return result;
478 } // eo read_file(const std::string&)
479
480
481 /**
482  * @brief writes a string to a file.
483  * @param path path to the file
484  * @param data the data which should be written into the file
485  * @param trunc set the trunc flag when opening the file. Do not use for files in /proc and /sys
486  * @return @a true if the data was written to the file.
487  *
488  * A simple (q'n'd) function for writing a string to a file.
489  */
490 bool write_file(const std::string& path, const std::string& data, bool trunc)
491 {
492     // set the correct openmode flags
493     std::ios_base::openmode flags = std::ios::out | std::ios::binary;
494     if (trunc)
495         flags |= std::ios::trunc;
496     
497     std::ofstream f( path.c_str(), flags);
498     if (f.good())
499     {
500         f.write( data.data(), data.size() );
501         return f.good();
502     }
503     else
504     {
505         return false;
506     }
507 } // eo write_file(const std::string&,const std::string&)
508
509
510 /**
511  * Copy file in 4k blocks from source to target.
512  * Overwrites the target if it already exists.
513  *
514  * On error the target file gets removed.
515  *
516  * @param src source file
517  * @param dest target file
518  * @return true if all is ok, false on error
519  */
520 bool copy_file(const std::string& src, const std::string& dest)
521 {
522     std::ifstream input( src.c_str(), std::ios::in | std::ios::binary );
523     if (!input)
524         return false;
525
526     std::ofstream output( dest.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
527     if (!output)
528         return false;
529
530     // Out of disc space?
531     if (!copy_stream(input,output))
532     {
533         output.close();
534         unlink(dest);
535         return false;
536     }
537
538     return true;
539 }
540
541 /**
542  * Copy streams in 4k blocks.
543  *
544  * @param is source stream
545  * @param os target stream
546  * @return true if all is ok, false on error
547  */
548 bool copy_stream(std::istream& is, std::ostream& os)
549 {
550    if (!is)
551       return false;
552
553    if (!os)
554       return false;
555
556    char buffer[4096];
557    while (is.good())
558    {
559       is.read(buffer, sizeof(buffer));
560       os.write(buffer, is.gcount());
561
562       // Can't write?
563       if (!os.good())
564          return false;
565    }
566
567    return true;
568 }
569
570 /**
571  * @brief returns the filename part of a path (last component)
572  * @param path the path.
573  * @return the last component of the path.
574  */
575 std::string basename(const std::string& path)
576 {
577     std::string::size_type pos= path.rfind('/');
578     if (pos != std::string::npos)
579     {
580         return path.substr(pos+1);
581     }
582     return path;
583 } // eo basename(const std::string&)
584
585
586 /**
587  * @brief returns the directory part of a path.
588  * @param path the path.
589  * @return the directory part of the path.
590  */
591 std::string dirname(const std::string& path)
592 {
593     std::string::size_type pos= path.rfind('/');
594     if (pos != std::string::npos)
595     {
596         std::string result(path,0,pos);
597         if (result.empty())
598         {
599             return ".";
600         }
601         return result;
602     }
603     return ".";
604 } // eo dirname(const std::string&)
605
606
607 /**
608  * @brief normalizes a path.
609  *
610  * This method removes empty and "." elements.
611  * It also resolves ".." parts by removing previous path elements if possible.
612  * Leading ".." elements are preserved when a relative path was given; else they are removed.
613  * Trailing slashes are removed.
614  *
615  * @param path the path which should be normalized.
616  * @return the normalized path.
617  */
618
619 std::string normalize_path(const std::string& path)
620 {
621     if (path.empty())
622     {
623         return std::string();
624     }
625     // remember if the given path was absolute since this information vanishes when
626     // we split the path (since we split with omitting empty parts...)
627     bool is_absolute= (path[0]=='/');
628     std::list< std::string >  parts;
629     std::list< std::string >  result_parts;
630     
631     split_string(path,parts,"/",true);
632     
633     for(std::list< std::string >::const_iterator it_parts= parts.begin();
634         it_parts != parts.end();
635         ++it_parts)
636     {
637         std::string part(*it_parts); //convenience..
638         if (part == std::string(".") )
639         {
640             // single dot is "current path"; ignore!
641             continue;
642         }
643         if (part == std::string("..") )
644         {
645             // double dot is "one part back"
646             if (result_parts.empty())
647             {
648                 if (is_absolute)
649                 {
650                     // ignore since we cannot move behind / on absolute paths...
651                 }
652                 else
653                 {
654                     // on relative path, we need to store the "..":
655                     result_parts.push_back(part);
656                 }
657             }
658             else if (result_parts.back() == std::string("..") )
659             {
660                 // if last element was already "..", we need to store the new one again...
661                 // (PS: no need for "absolute" check; this can only be the case on relative path)
662                 result_parts.push_back(part);
663             }
664             else
665             {
666                 // remove last element.
667                 result_parts.pop_back();
668             }
669             continue;
670         }
671         result_parts.push_back(part);
672     }
673     std::string result;
674     if (is_absolute)
675     {
676         result= "/";
677     }
678     result+= join_string(result_parts,"/");
679     return result;
680 } // eo normalize_path(const std::string&)
681
682
683 /**
684  * @brief calls fsync on a given directory to sync all it's metadata
685  * @param path the path of the directory.
686  * @return true if successful
687  */
688 bool dirsync(const std::string& path)
689 {
690     // sync the directory the file is in
691     DIR* dir=opendir(path.c_str());
692     if (dir == NULL)
693         return false;
694
695     int ret=fsync(dirfd(dir));
696
697     closedir(dir);
698
699     return (ret==0);
700 }
701
702 /**
703  * @brief changes the file(/path) mode.
704  * @param path the path to change the mode for.
705  * @param mode the new file mode.
706  * @return @a true iff the file mode was sucessfully changed.
707  */
708 bool chmod(const std::string& path, int mode)
709 {
710     int res= ::chmod(path.c_str(), mode);
711     return (res==0);
712 } // eo chmod(const std::string&,int)
713
714
715 /**
716  * @brief changed the owner of a file(/path)
717  * @param path the path to change the owner for.
718  * @param user the new file owner.
719  * @param group the new file group.
720  * @return @a true iff the file owner was succesfully changed.
721  *
722  * @note
723  * the validity of user and group within the system is not checked.
724  * This is intentional since this way we can use id's which are not assigned.
725  */
726 bool chown(const std::string& path, const I2n::User& user, const I2n::Group& group)
727 {
728     uid_t uid= user.Uid;
729     if (uid<0) return false;
730     gid_t gid= group.Gid;
731     if (gid<0) gid= user.Gid;
732     if (gid<0) return false;
733     int res= ::chown( path.c_str(), uid, gid);
734     return (res==0);
735 } // eo chown(const std::string&,const User&,const Group&)
736
737 /**
738  * Recursive delete of files and directories
739  * @param path File or directory to delete
740  * @param keep_parent_dir Keep parent directory (=empty out directory) [optional]
741  * @param error Will contain the error if the return value is false [optional]
742  * @return true on success, false otherwise
743  */
744 bool recursive_delete(const std::string &path,
745                       bool keep_parent_dir,
746                       std::string *error)
747 {
748     bool rtn = true;
749
750     try {
751         struct stat my_stat;
752         if (stat(path.c_str(), &my_stat) != 0) {
753             throw runtime_error("can't stat " + path);
754         }
755
756         if (S_ISDIR(my_stat.st_mode)) {
757             DIR *dir = opendir(path.c_str());
758             if (!dir) {
759                 throw runtime_error("can't open directory " + path);
760             }
761
762             struct dirent store, *entry = NULL;
763             while (readdir_r(dir, &store, &entry) == 0 && entry != NULL)
764             {
765                 string filename = entry->d_name;
766                 if (filename == "." || filename == "..") {
767                     continue;
768                 }
769
770                 // Delete subdir or file.
771                 rtn = recursive_delete(path + "/" + filename, false, error);
772                 if (rtn == false) {
773                     break;
774                 }
775             }
776
777             closedir(dir);
778             if (keep_parent_dir == false && !rmdir(path)) {
779                 throw runtime_error("can't remove directory " + path);
780             }
781         } else {
782             if (!unlink(path)) {
783                 throw runtime_error("can't unlink " + path);
784             }
785         }
786     } catch (exception &e) {
787         if (error) {
788             ostringstream out;
789             out << e.what() << " (" << strerror(errno) << ")";
790             *error = out.str();
791         }
792         rtn = false;
793     } catch (...) {
794         if (error) {
795             ostringstream out;
796             out << "unknown error (" << strerror(errno) << ")";
797             *error = out.str();
798         }
799         rtn = false;
800     }
801
802     return rtn;
803 } // eo recursive_delete(const std::string&,std::string*)
804
805 /**
806     Create a unique temporary directory from path_template.
807     @param Path template. The last six characters must be XXXXXX.
808     @param error Will contain the error if the return value is false [optional]
809     @return Name of new directory or empty string on error.
810 */
811 std::string mkdtemp(const std::string &path_template, std::string *error)
812 {
813     boost::scoped_array<char> buf( new char[path_template.size()+1] );
814     path_template.copy(buf.get(), path_template.size());
815     buf[path_template.size()]=0;
816
817     char *unique_dir = ::mkdtemp(buf.get());
818     if (!unique_dir)
819     {
820         if (error)
821             *error = strerror(errno);
822         return "";
823     }
824
825     // Scoped pointer is still valid
826     return std::string(unique_dir);
827 }
828
829 /**
830     Create directory
831     @param path Path to create
832     @param error Will contain the error if the return value is false [optional]
833     @return True on success, false on error
834 */
835 bool mkdir(const std::string &path, const mode_t &mode, std::string *error)
836 {
837     if ( ::mkdir(path.c_str(), mode) == 0)
838         return true;
839
840     if (error)
841         *error = strerror(errno);
842     return false;
843 }
844
845 /**
846     Remove directory
847     @param path Path to removed
848     @param error Will contain the error if the return value is false [optional]
849     @return True on successs, false otherwise
850 */
851 bool rmdir(const std::string &path, std::string *error)
852 {
853     if ( ::rmdir(path.c_str() ) == 0)
854         return true;
855
856     if (error)
857         *error = strerror(errno);
858     return false;
859 }
860
861 /// Small helper class for scoped free
862 class scoped_C_free
863 {
864 public:
865     scoped_C_free(void *ptr)
866         : pointer_to_free(ptr)
867     {
868     }
869
870     ~scoped_C_free()
871     {
872         free (pointer_to_free);
873         pointer_to_free = NULL;
874     }
875
876 private:
877     void *pointer_to_free;
878 };
879
880 /**
881     Get current working directory
882     @return Current working directory. Empty string on error.
883 */
884 std::string getcwd()
885 {
886     char *cwd = ::getcwd(NULL, 0);
887     if (!cwd)
888         return "";
889
890     // Make deallocation of cwd exception safe
891     scoped_C_free holder(cwd);
892
893     string current_dir(cwd);
894     return current_dir;
895 }
896
897 /**
898     Change current working directory
899     @param path Path to change to
900     @param error Will contain the error if the return value is false [optional]
901     @return True on successs, false otherwise
902 */
903 bool chdir(const std::string &path, std::string *error)
904 {
905     if ( ::chdir(path.c_str() ) == 0)
906         return true;
907
908     if (error)
909         *error = strerror(errno);
910     return false;
911 }
912
913 /**
914     Set file mode creation mask
915     @param mask Creation mask
916     @return Previous creation mask (function call always succeeds)
917 */
918 mode_t umask(mode_t mask)
919 {
920     return ::umask(mask);
921 }
922
923
924 /**
925  * @brief Remove unlisted files
926  *
927  * @param directory Directory to look for files
928  * @param keep_files List of files or directories to keep
929  * @param prefix Filename prefix to match. Empty prefix matches all.
930  *
931  * @return bool True if the directory was scanned, false on error (directory not found, permission denied)
932  **/
933 bool remove_unlisted_files(const std::string &directory,
934                            const std::set<std::string> &keep_files,
935                            const std::string &prefix)
936 {
937     std::vector<std::string> content;
938     if (!get_dir(directory, content, false))
939         return false;
940
941     bool all_fine = true;
942     BOOST_FOREACH(const std::string &file, content)
943     {
944         // Check for filename prefix (if any)
945         if (!prefix.empty() && file.find(prefix) != 0)
946             continue;
947
948         // Check if file is whitelisted
949         if (keep_files.find(file) != keep_files.end())
950             continue;
951
952         // Try to unlink file. (Continue on error)
953         if (!unlink(directory + "/" + file))
954             all_fine = false;
955     }
956
957     return all_fine;
958 }
959
960 /**
961  * @brief Get free size in bytes on a given path or filename
962  *
963  * @param path Directory or filename to look in
964  *
965  * @return Number of bytes available to a regular user, -1 in case of an error
966  **/
967 long long get_free_diskspace(const std::string& path)
968 {
969     struct statvfs sf;
970
971     int ret;
972     while ( ((ret=statvfs(path.c_str(),&sf)) == -1) && (errno==EINTR) );
973
974     if (ret==-1)
975     {
976         // a real error occured
977         return -1;
978     }
979
980     long long free_bytes=0;
981
982     // load block size
983     free_bytes=sf.f_bsize;
984
985     // multiply by number of free blocks accessible by normal users
986     // make sure we really multiply long long by long long and don't overflow at 2 GB
987     free_bytes*=(long long)sf.f_bavail;
988
989     return free_bytes;
990 }
991
992
993 } // eo namespace I2n