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