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