Filesystem Class: Paths, SIMPLIFIED

Pages: 12
I just finished polishing a class that will simplify the use of filesystems.

It uses BOOST library, and I know what you're going to say, but hear me out. No member of the class emplements a boost function, variable type, or namespace of any kind. All boost operations are performed in seperate functions.

so, fsys_class::copy
the function that uses boost: int copy_sup(), meaning copy supliment.

I've clocked it, and it uses (at max) 500Kb of memory (which is better than the 2000Kb of the previous class). At minimum it runs with around 400Kb. This is about as much as my programs that don't use classes (i wrote those a long while back, but have since learned a lot).

I've also looked at the recursive directory iterator's memory efficiency, and it is quite astonishing how efficient it is when seperated from the class.

The class also has an option: use the system() command. Whilest insecure, it does provide an extra, fool-proof way to execute an action. Because it is insecure, System() is only used if a bool in the class is set to true (default is false), and even then it is used as a last resort, and all else fails.

fsys_class.cpp: http://pastebin.com/2wJgdFWC
fsys_class.h: http://pastebin.com/RXnzyeDS

Tell me what you think.
*****************************************************************************

Notes:

The goal was to simplify file/folder operations, while maintaining efficiency, as well as assuring no-fail method.

I also wanted to make it easy t treat paths more like objects, as though your dragging and droping the files/folders.

so, for example:

1
2
fsys_class p = "C:\myfolder";
p.move_to("C:\program files");


so, now myfolder is under program files.

I tried to make it feel a lot more natural.

When you move, copy, or create a path (create only for folders) it will do so under fsys_class::path.

Any path can be used for copy, move, rename, and del. Create can only be done for folders.

If an error occurs during this process, and the folder/file copy, move, delete, rename fails to execute, it returns 1. It it succeeds, it returns 0.

Note: an error is only returned if the function could not carry out an action, but not if the function succeeeds, which means the error is not indicative of whether or not the copy/del/create/rename/move actually modified the file in any way. I may add that later on (i am wrapped up in college right now...)

I also added is_file and is_folder, which check for the existence of the path as a file, or as a folder.

In the class, there is a private variable: fsys_class::path. This variable represents the path the class is 'equal' to. It's default is the current path, returned by the boost function: current_path().

There are also 2 functiosn that retrieve vaules from the private variable fsys_class::path: fsys_class::gpath(), and fsys_class::gilename. Because the path can be modified by invoking the public constructors, the variable is not public.

reasons for this: 1. Modification should not happen to the variable, but to the class

2. This is basic information retrieval. Items I've needed to have are the filename and the full path, hence the functions.

sub_stuff returns a vector of the strings that represent the paths under the current path, if the path is a folder.
*****************************************************************************

I think that covers everything. Have fun, and tell me what you think.

Also, this is for Windows OS.
Last edited on
I didn't read your post because it was too long. Sorry.

A couple of things regarding the function listed below...

1) The "safety precautions" are overkill. Four times you check is_regular_file(p)? Whatever you're checking for... one time should be enough.

2) Doesn't Boost have some removal function that you can use instead of C's std::remove() and std::system()?

Also, this is for Windows OS.


Well let go of std::system() and it will no longer be for Windows only!

I hope you get enough feedback to perfect this, then put it in the Articles. I must say, however, that my gut feeling is that you are reinventing the wheel and Boost already provides this functionality. Then again, it's just a hunch.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/* This function is littered with safety precautions so that we
 don't fail.  Useing the system command line is an option, and only available
 as a last resort, and only if use_sys == true*/
int delete_file_sup(const string& s, const bool& use_sys)
{
    boost::filesystem::path p = s;
    bool sys = use_sys;
    string command = ("DEL /F /Q \"" + s + "\" >nul 2>nul");
   
    //if the path is a file
    if(boost::filesystem::is_regular_file(p) == true)
    {
        try
        {
            remove(s.c_str());
        }
        catch(const exception& e)
        {
            cout<< e.what()<< endl;
            if((boost::filesystem::is_regular_file(p) == true) && (sys == true))
            {
                system(command.c_str());
                if(boost::filesystem::is_regular_file(p) == true)
                {
                    return 1;
                }
            }
            if(sys == false)
            {
                return 1;
            }
        }
    }
   
    //just being fool-proof here...
    if(boost::filesystem::is_regular_file(p) == false)
    {
        return 0;
    }
    return 1;
}
I think you should be willing to fail and throw an exception, rather than having all these fallbacks. You're not writing the operating system for a nuclear power station or a fighter jet. Hell, even that software probably allows errors to happen in its deep, low-level code, so long as the errors are always reported to the calling functions, because that code needs to be kept simple and efficient. Error-recovery would be handled in the higher-level code because there the program can command the "bigger picture". Imagine low-level functions as workmen building a house and the higher level code as the foreman. If a bricklayer tries to lay some bricks and fails for some reason, he doesn't run off and quarry some rock and see if he can get that to fit instead, and then when that fails start trying to make bricks out of mud. No, he goes to the foreman and tells him there's a problem. The foreman can see the bigger picture, but he doesn't pay attention to minor details. The workmen pay attention to the details, but they have a limited worldview - no bigger picture. The foreman co-ordinates the work, the workmen do it. That's how software should work. So, your work-doing functions should be lazy and throw exceptions or return error values when they can't do what they need to do. The code that co-ordinates those low-level functions solves the problems that arise (in commercial-and-below grade software, "solves" usually means "prints a cryptic error message and exits").
Last edited on
"DEL /F /Q \"" + s + "\" >nul 2>nul"
I like how if this command was translated to sh and ran on UNIX, one could get a file deletion function to run arbitrary commands.
(deleted)
Last edited on
it HAS to be. I want to be able to use the class, and trust that NO MATTER WHAT, the files/folders will be copied/renamed/ etc
I'm just curious why you think that deleting a file via Boost is different than doing it via shell commands. Do you think that the system has two different functions that delete files by different methods?
If deleting a file fails, it's likely due to permission or locking problems. Calling DeleteFile() after it has already failed hoping it will somehow not fail again is one definition of insanity.
The only way to sort of guarantee that a file operation is performed in a multitasking system is to try to do it and, if it fails, display a notification to let the user know what failed, what might fix it, and to let them retry the operation (after trying a fix) or abort execution.

This class gets rid of the exceptions.
If you just wanted no exceptions a simple wrapper would have sufficed:
1
2
3
4
5
6
7
8
bool try_make_snafucated(Foo bar){
    try{
        snafucate(bar);
    }catch (exceptionally_fubarry &){
        return 0;
    }
    return 1;
}
Returning non-zero, non-error-status on failure is pretty lousy, by the way.

I do not want a dev looking at my class, because it threw an exception and stopped the program, hence the fail safes.
Returning an error code that's more easily ignored, allowing the program to go on in an inconsistent state is better, then?

For scanning a filesystem, or whatever, look to boost. I am not re-writing boost, I'm using it to create a simplified, higher-level tool.
Actually, Boost is higher level. Returning error codes is what system-level functions do. Boost wraps those and instead throws exceptions. This is, in fact, an unabstraction layer.
Last edited on
Helios says:
Returning non-zero, non-error-status on failure is pretty lousy, by the way.


I'm not a professional, I am learning, by the way.
Also, I do not care for exceptions in this case. I wrote this because the only real error that can happen is on the user-end. (we assume the program is compatible with the system it is eing run on)

Also, do NOT assume Boost is used for everything. CHECK OUT THE CLASS. The deletion is done by remove(), not boost. Boost is only used when deleting directories, because I do not need to write an algorithm which is readily available.

DO I REALLY HAVE TO PULL OUT THE DOCUMENTATION: boost uses remove() as well.
uintmax_t remove_all(const path& p);
uintmax_t remove_all(const path& p, system::error_code& ec);

    Effects:  Recursively deletes the contents of p if it exists, then deletes file p itself, as if by ISO/IEC 9945 remove().

        [Note: A symbolic link is itself removed, rather than the file it resolves to being removed. -- end note]

    Postcondition: !exists(p)

    Returns: The number of files removed.

    Throws: As specified in Error reporting.


Don't tell me using boost is like using the command line, because it is absolutely not (I will, however, listen if you provide imperical evidence that it does (current version)).

Helios says::
why you think that deleting a file via Boost is different than doing it via shell commands


I'm sorry, are you under the impression that they are the same? They are not. Using a shell command calls a shell with the same priveledges as the program. This is a security issue. Boost calls remove without starting another shell, which could be easily replaced by a malicious program.

Helios says:
Calling DeleteFile() after it has already failed hoping it will somehow not fail again is one definition of insanity


Yeah, except I've run code that fails twice or more, and I have gotten different results each time it fails (different reasons, somtimes success on the second time). I do not think that a second attempt is over the top, especially considering some results I've recieved in the past. I've had algorithms fail, simply because of lag. All they needed was to be run again. I say, if you fail, try again and if the second time fails, somthing is wrong. It's called confirming a problem. Even if it is a machine, we should not assume it is absolutely perfect in every way, because it is not.

I'm sorry if this is not to your taste. I asked for the critisism of my work, not whether you thought this or that. Support your arguments with imerical evidence, and undeniable fact. Unobjective critisism will be ignored, as it is unproductive for the controversial mess it creates.

Sorry if I seem a bit heated, but the subject matter does imply the necessity of examining my code before making a comment on it, does it not? (seriously, doesn't it?)
Last edited on
WHAT'S WITH THE UPPERCASE LETTERS?

The deletion is done by remove(), not boost.
boost uses remove() as well.

Wrong. Also I'm looking into the Boost code right now.

Effects: Recursively deletes the contents of p if it exists, then deletes file p itself, as if by ISO/IEC 9945 remove().

I think they mean the effect is similar to that of remove() -- not that they used remove() itself.

Don't tell me using boost is like using the command line, because it is absolutely not.

Misunderstanding. He says that a file can only be deleted one way regardless of what function you use. So if the first attempt fails, the others will as well... unless problem goes away in between the different function calls.

I suggest this: create a text file with an Administrator account.
Then enter the Guest account (activate it from the Control Panel if it doesn't show) and try to use your program to delete the file.

This is to prove you should rather use a single function, and if it fails let the user know.
Last edited on
Suppose you want some soup from a place that opened down the street, and you have two paths to take there. Now, the cook is a bit eccentric and there's a chance that he'll refuse to serve you, depending on a die roll. Does the path you take to the shop affect your chances to get "no soup for you!" shouted at you? No. The decision depends the cook's die, not your path.

The same is true here.

Eventually, any attempt to delete a file must pass through the OS routine that manipulates the file system (that is, the physical layout of bits on the storage device) to remove an entry from it. In the case of Windows NT, that function is NtDeleteFile(). The particular path of functions that gets you to that function doesn't really matter. So yes, using the command line is the same as using Boost, in that one succeeds if and only if the other one would also have succeeded.
File system functions will generally fail in one of four cases: invalid arguments (e.g. your path is malformed or doesn't point to the correct kind of file, or any file at all), incorrect permissions (e.g. the file is protected or your program doesn't have write permissions), file system corruption or hardware failure. None of these are problems that will magically go away in between function calls, unless the user is Dr. Manhattan. All you can do about errors like these is inform the user that something went wrong. Going back to the analogy in my previous post; if the workmen (functions) consistently complain to the foreman (program) that the bricks crack easily and can't be used, and the foremen of several projects complain to the building company (you), then the company can solve the problem. Similarly, if the user is consistently informed that filesystem functions are returning error codes, then they know that something about their system is broken or misconfigured.

Catfish4 wrote:
I think they mean the effect is similar to that of remove() -- not that they used remove() itself.

I don't know whether Boost uses remove or not, but when they say "it behaves as if it does x" that's to allow for different implementations: most implementations will do it that way, but they don't have to; the only requirement is that no-one can tell the difference.
(deleted)
Last edited on
If you recognise that, on failure, the worst that can happen is nothing, and that all these APIs lead directly to the OS which either fails or it doesn't, and that if the OS fails in one case then it will fail in all comparable cases, then why are you writing all these fallbacks? They're unnecessary.

IWishIKnew wrote:
I have been able to delete files with non-admin privelidge command prompt, that a non-admin privilidge program of mine was unable to delete.

Maybe your command prompt had higher privileges than your program did. Windows' process permissions are more complex than "admin" and "not admin".

The problem, however, is that I have observed different behavior (heuristic patterns) from each

That's not what heuristics are. A heuristic is an approximation of an algorithm that gets approximate solutions where a correct algorithm is too slow or can't find a solution.
chrisname wrote:
Maybe your command prompt had higher privileges than your program did. Windows' process permissions are more complex than "admin" and "not admin".


I don't believe so, but it doesn't matter. it's an option, and you don't have to use it.

UPDATE:

I have revisesed the class to return error values:

return FSYS_ERROR;
An exception is thrown or an operation failed (mid-execution).

return FSYS_FAILURE;
The operation failed. This is returned after the operation 'succeeds', but the desired result was not accomplished. AKA- copy_to did not throw exceptions, yet it did not actually copy the file.

errors are displayed, so a simple if statement can be used to pause the program if you want to see them.

upon failure, though, you may want to inform the user.

Invalid paths are not returned as errors. That is supported through the member functions is_folder() and is_file().
Last edited on
It is logical that all functions do pass through the OS. The problem, however, is that I have observed different behavior (heuristic patterns) from each. In some cases, a command line will be able to get a job done, that a function call won't. I can't explain it, it is just what I have observed.
What you're doing is known as voodoo programming, then. You attempt to do something by means that are believed to be magical. In your case, the particular magic is called "heuristics". Magic is what fills a vacuum of knowledge of a system (both in computing and elsewhere).
I mis-used the term.

But it is not 'magical'. I have actually tested it.

When I use the following:

DEL /F /Q [filename]

on a file where
remove([filename])
failed, it succeeds.

Sometimes it does not succeed though, like in cases of user authentication. However, in cases where an exception of access denied is thrown, a command line will succeed. Because of this, I have added the option to use system, because it can remove files where an 'access denied' exception is thrown, and a file can't be removed.

I have performed many-a-backup (30-64 Gb), which includes MinGW, and a few programs. Exceptions are thrown (under administrative privelidges, non-the-less) which prevent my program from deleting the rest of these files. When the command line, however, is used, the files are swiftly done away with.

If you want me to explain why, I'm sorry if I don't have an answer. Mabey the command line executes a different command with some arguments remove() doesn't use.

I think it has somthing to do with file properties, though, because when I looked into it, there were some odd security descriptors. I do have windows 8, and it stands to reason that this could happen, since I actually also can't even delete the damn app files (seriously, Microsoft could learn a thing or two from W7....), which tells me there is some sort of higher user authentication/privelidges. It could also e that the OS isn't programmed to let me delete them, in which case I hop microsoft is reading this: You assholes (to Microsoft)! (i want to delete them, cause they waste soooo much space and I don't use them, and they aren't deleted by uninstall, but that;s another story for later)
Last edited on
Sorry, I guess I did ask for your opinions, and I'm being... a duche.

Regardless of how the functions work, what is the reason for not wanting to use 2 differently emplemented algorithms? (I mean, for all I know, system does extra stuff before it throws the cammand over to the OS, right?)

Why should I let it throw exceptions?

and

What is wrong with having the program try a little bit before giving up on the operation? I thought it would be prudent to have it try an operation at least twice before it went to a different method, or returned an error. As they say in science: nothing is definitive unless you can replicate the results indefinitely. So, if the result is the same, then try somthing else or give up.

Tell me if I'm wrong. Sorry for reacting poorly.
IWishIKnew wrote:
Regardless of how the functions work, what is the reason for not wanting to use 2 differently emplemented algorithms?

This is like asking "why not sort the same dataset with two different sorting algorithms"? Because it's a waste of time.

Here is the mindset I'm under:

If a big phillips head doesn't work on a phillips head screw, should I give up because they are the same type or should I try the same screwdriver with a slightly different size?

I'm under the impression that file system properties play a role in the way files/folders are handled. I'm also under the impression that system() and remove() handle these properties in different ways. Is this wrong, then?
If you want posix functionality and/or portability, use remove (or the boost (or tr2) equivalent.) If you don't, use the O/S API directly. There isn't much point in using one when you actually want the other and using system is a very poor way to go about avoiding the O/S API as it's not as efficient as directly using the API, is equally unportable and is also a security risk.
IWishIKnew wrote:
If a big phillips head doesn't work on a phillips head screw, should I give up because they are the same type or should I try the same screwdriver with a slightly different size?

If you only have one screwdriver head, then yes, you should give up: changing the handle won't make it fit the screw. There is only one screwdriver head in this scenario: the operating system's function for deleting files. All you're doing by trying the command-line is using a different handle with the same head.
Pages: 12