How to check if a file can be deleted - file

I want to use it during uninstall procedure to warn the user ahead.
The procedure should work for W2000+, hence Vista API's are not allowed.
This seems to catch some conflicts:
if( GetFileAttributes( lpPath ) == INVALID_FILE_ATTRIBUTES )
{
// File does not exist
}
else
{
BOOL bCanDelete = FALSE ;
HANDLE hFile = CreateFile( path,
GENERIC_WRITE /*|DELETE*/,
0 /*FILE_SHARE_DELETE*/,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if( hFile != INVALID_HANDLE_VALUE )
{
DWORD size = 10000 ; // a number > file size allowed
if( size != INVALID_FILE_SIZE )
{
if( LockFile( hFile, 0,0, size,0) )
{
UnlockFile( hFile, 0,0, size,0) ;
bCanDelete = TRUE ;
}
}
CloseHandle( hFile ) ;
}
}
Namely it detects these situations:
a) Deleting running exe file
b) Deleting opened pdf
Using GENERIC_WRITE|DELETE seems to behave similarly.
Using DELETE alone works for the case b), but not for a).
I have no positive evidence that LockFile() catches any meaningful conflict, but suppose it does.
Does anybody have a better idea?

First point: unless you take steps to prevent it, nearly anything you report may change between the time you test, and the time you try to take action based on that test (e.g. after you've tried to check that you can delete it, the user might change it to 'read-only').
To get a meaningful result, instead of using DeleteFile to delete the file, I'd use CreateFile with FILE_SHARE_DELETE and the FILE_FLAG_DELETE_ON_CLOSE flag. If you can't open the file this way, it gives a pretty good clue that you won't be able to delete it. If you can open it this way, then simply closing the handle will delete the file -- and nothing else will be able to open it in the interim unless it also specifies FILE_SHARE_DELETE (and even if it does, when the last handle to the file is closed, the file will be deleted, so if it's not deleted immediately, it will be soon).

I'm not a C++ programmer, but you could try to rename that file. If you can do that, you probably can delete it.

Related

How to identify whether a particular file is called first time in C

While executing a C program,A debug print should be inserted to a log file (say log.txt),
but this C program is executed from various places.
So, multiple logs were found in log.txt
I want that debug to be present only once in the log.txt.
How to insert a check in C program to achieve the above requirement
I have tried with static variable, but it works only if for the function called first time
not the file.
whether access function would help this scenario??
You need some way for the programs to store that they've done a thing, know as a semaphore.
Simplest thing to do is use a shared location like /var/run/ to place a semaphore file. Then each program attempts to create that file. If it already exists, they can assume someone else already performed the action.
To do that we use the open system call (not fopen) for fine control. Using both the O_CREAT and O_EXCL flags says to create the file, and error if it already exists. This is atomic, we're checking if the file exists and creating it in one action. There's no way two programs could accidentally check & create at the same time.
#include <stdio.h>
#include <fcntl.h>
#include <stdbool.h>
bool check_semaphore(const char *file) {
if( open(file, O_CREAT|O_EXCL) >= 0 ) {
return true;
}
else {
return false;
}
}
int main() {
if( check_semaphore("did_debug_log") ) {
FILE *log = fopen("log.txt", "a");
if( !log ) {
perror("Couldn't open log.txt");
}
fprintf(log, "Some debug stuff\n");
}
}
You can use more sophisticated shared storage as a semaphore, like shared memory or a shared database such as SQLite, but the basic technique is the same. Check if the operation has been complete while also declaring you're going to do it in an atomic operation.

stat(), fstat(), lstat(), and fopen(); how to write TOCTOU protected system independent code

I've been dealing with a problem for a few weeks now updating 20 year code that needs to be system independent (work on both Linux and Windows). It involves Time-of-Check, Time-of-Use (TOCTOU) issues. I made a thread here, but it didn't go very far, and after ruminating on it for a while and searching deeper into the problem, I think I understand my question a bit better. Maybe I can ask it a bit better too...
From what I've read, the code needs to check if the file exists, if it is accessible, open the file, do some operations and finally close the file. It seems the best way to do this is a call to lstat(), a call to fopen(), a call to fstat() (to rule out the TOCTOU), and then the operations and closing the file.
However, I've been lead to believe that lstat() and fstat() are POSIX defined, not C Standard defined, ruling out their use for a system agnostic program, much in the same way open() shouldn't be used for cross-compatibility. How would you implement this?
If you look at my first post, you can see the developer from 20 years ago used the C preprocessor to cut the code into cross-compatible parts, but even if I did that, I wouldn't know what to replace lstat() or fstat() with (their windows counterparts).
Edit: Added abreviated code to this post; if something is unclear please go to the original post
#ifdef WIN32
struct _stat buf;
#else
struct stat buf;
#endif //WIN32
FILE *fp;
char data[2560];
// Make sure file exists and is readable
#ifdef WIN32
if (_access(file.c_str(), R_OK) == -1) {
#else
if (access(file.c_str(), R_OK) == -1) {
#endif //WIN32
char message[2560];
sprintf(message, "File '%s' Not Found or Not Readable", file.c_str());
throw message;
}
// Get the file status information
#ifdef WIN32
if (_stat(file.c_str(), &buf) != 0) {
#else
if (stat(file.c_str(), &buf) != 0) {
#endif //WIN32
char message[2560];
sprintf(message, "File '%s' No Status Available", file.c_str());
throw message;
}
// Open the file for reading
fp = fopen(file.c_str(), "r");
if (fp == NULL) {
char message[2560];
sprintf(message, "File '%s' Cound Not be Opened", file.c_str());
throw message;
}
// Read the file
MvString s, ss;
while (fgets(data, sizeof(data), fp) != (char *)0) {
s = data;
s.trimBoth();
if (s.compare( 0, 5, "GROUP" ) == 0) {
//size_t t = s.find_last_of( ":" );
size_t t = s.find( ":" );
if (t != string::npos) {
ss = s.substr( t+1 ).c_str();
ss.trimBoth();
ss = ss.substr( 1, ss.length() - 3 ).c_str();
group_list.push_back( ss );
}
}
}
// Close the file
fclose(fp);
}
The reliable way to check whether the file exists and can be opened is to try opening it. If it was opened, all was OK. If it was not opened, you can think about spending time to analyze what went wrong.
The access() function formally asks a different question from what you think; it asks 'can the real user ID or the real group ID access the file', but the program will use the effective user ID or the effective group ID to access the file. If your program is not running SUID or SGID, and was not launched from a program running SUID or SGID — and that's the normal case — then there's no difference. But the question is different.
The use of stat() or
lstat() doesn't seem helpful. In particular, lstat() only tells you whether you start at a symlink, but the code doesn't care about that.
Both the access() and the stat() calls provide you with TOCTOU windows of vulnerability; the file could be removed after they reported it was present, or created after they reported it was absent.
You should simply call fopen() and see whether it works; the code will be simpler and more resistant to TOCTOU problems. You might need to consider whether to use open() with all its extra controls (O_EXCL, etc), and then convert the file descriptor to a file pointer (fdopen()).
All of this applies to the Unix side.
The details will be different, but on the Windows side, you will still be best off trying to open the file and reacting appropriately to failure.
In both systems, make sure the options provided to the open function are appropriate.

Open a file for edit with $EDITOR variable from a C program

I'm writing a C program and I want to open a file with the $EDITOR variable to make changes to it.
Assuming that I already checked if the $EDITOR is not set and that the part missing is to open the file for edit, is execl() the best option or should I use a different function?
I'm working under Arch linux.
Provided you are reasonably sure that you are in a single-threaded program, you have standard input and standard output (and probably standard error too) going to a terminal, and you aren't going to be upset by the signal handling imposed on you by system(), you can use system() to execute the command via a shell.
If you don't want to trust system(), then one of the exec*() family of functions (plus fork()) will do what you need. You still need to be reasonably sure about the standard I/O channels — some editors do not react well to being given random files or piped input. You get to choose what signal handling to use — and how it is installed. You can get to deal with any thread-safety issues. It is a moderate amount of work.
You probably want to think carefully about whether you give the user the 'real' file to edit or a copy of it. Your code should probably recognize whether the editor exited successfully (and should probably ignore the output file if it did not exit successfully). You may also want to check whether the new version of the file is sensibly sized (not zero bytes, for example — but maybe that doesn't matter; it depends on the context). If the file being edited is a precious configuration file, you worry about this; if it is some previous commands to be re-executed (a history mechanism), you worry less about some of these details.
This is the history 'edit' command in a program of mine. It allows a user to specify a range of commands to be copied into a file, which is then edited, and the result (which may be empty) is then executed. It is code verbatim. Most of the function calls are to program-specific functions, but most of the names should be interpretable (I think). The ctxt_*() family of functions handle 'context', the current settings for the program. It works with more environment variables than yours needs to. The sql_file() function executes the commands from the input file in the current context — this code creates a new context in which to run the commands.
/* Edit history command(s) */
static void do_edit(char *s)
{
FILE *fp;
long c1;
long c2;
char tmpfname[BUFSIZ];
char sys[BUFSIZ];
const char *editor;
if (ctxt_gethistory() != OP_ON)
{
cmd_warning(E_HISTORYOFF, "");
return;
}
s = skipblanks(s);
c1 = c2 = 0;
if (sscanf(s, "%ld%ld", &c1, &c2) != 2)
c2 = c1;
if ((fp = fopen_namedtmpfile(tmpfname, sizeof(tmpfname))) == 0)
{
cmd_warning(E_FAILCREATETMPFILE, "");
return;
}
hist_output(fp, c1, c2, H_COMMAND);
fclose(fp);
if ((editor = getenv("DBEDIT")) == NIL(char *) &&
(editor = getenv("VISUAL")) == NIL(char *) &&
(editor = getenv("EDITOR")) == NIL(char *))
editor = DEF_EDITOR;
esnprintf(sys, sizeof(sys), "%s %s", editor, tmpfname);
system(sys);
fp = fopen(tmpfname, "r");
unlink(tmpfname);
if (fp == 0)
{
cmd_warning(E_FAILREOPENTMPFILE, tmpfname);
}
else
{
/* Copy file to history log */
if ((c1 = hist_input(fp)) > 0)
cmd_set_promptnum(c1);
fseek(fp, 0L, SEEK_SET);
ctxt_newcontext();
ctxt_newinput(fp, "<<temp>>");
ctxt_sethistory(op_off);
sql_file();
ctxt_endcontext();
}
}

C: Thread Safe Logging to a File

I am trying to write a thread-safe C logging function, and I am having some serious problems with the file IO. So, basically, I start with an interesting fopen call that lets me open the log in binary update mode:
FILE *log, *start;
int timeout = 0, error;
//make file (fails if file exists)
log = fopen(LOG_FILE, "wx");
//Close the file if a new one was created
if(log)
fclose(log);
//Open file in update mode (it must exist for this)
log = fopen(LOG_FILE, "rb+");
Next, I lock the file, incorporating a timeout if another thread locks it for too long:
//Init other file pointer
start = log;
//Lock file (with timeout)
rewind(start);
error = lockf(fileno(start), F_TLOCK, 0);
while( error == EACCES || error == EAGAIN)
{
//sleep for a bit
usleep(LOCKED_FILE_RETRY_TIME);
//Incremement timeout
timeout += LOCKED_FILE_RETRY_TIME;
//Check for time out
if(timeout > LOCKED_FILE_TIMEOUT)
{
return;
}
//Retry the lock operation
error = lockf(fileno(start), F_TLOCK, 0);
}
And finally, I add the required message to the end of the file, unlock it and close the file:
//Print log entry
fseek(log, 0, SEEK_END);
fwrite((const void*) log_msg, 1, strlen(log_msg), log);
//Unlock the block
rewind(start);
lockf(fileno(start), F_ULOCK, 0);
//Close file
fclose(log);
However, it seems like the majority of the messages are overwritten in the log, rather than appended, almost as if fopen took a "snapshot" of the file, waited for it to be unlocked, and wrote to where the end of the file would have been if another process didn't add to it. Does anyone have any idea as to how I could go about fixing this problem?
As an aside, I want to be in binary update mode because I will eventually add some trimming functionality that will ensure the log file does not exceed a certain size, and this is easier for me to accomplish with working fseek calls and R/W functionality.
Any tips are appreciated. Thanks beforehand!
You didn't call fflush() on the file pointer before unlocking. That way your log message remained in a stdio buffer, to be written out in fclose(), at a time when the lock is no longer being held.
To fix the problem, either add an fflush(log) before the unlock operation, or simply move the fclose(log) before it.

CreateFile always override the specified file

I'm trying to log the actions made by a service I wrote using the Windows-API & C-language, so I made a log file system.
The problem is that at each CreateFile call, the file is overridden instead of just opening it and write at the end of the file.
Here's the code of my WriteInLogfile function :
void WriteInLogFile(LPCTSTR log_string)
{
HANDLE hFile;
DWORD dBytesWritten;
if ((hFile = CreateFile(LOG_FILE_PATH, GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE
&& (GetLastError() == ERROR_FILE_NOT_FOUND))
{
if ((hFile = CreateFile(LOG_FILE_PATH, GENERIC_WRITE, 0, NULL,
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE)
{
if (!WriteFile(hFile, log_string, strlen(log_string), &dBytesWritten, NULL))
aff_error("WriteInLogFile");
CloseHandle(hFile);
}
}
else
{
if (!WriteFile(hFile, log_string, strlen(log_string), &dBytesWritten, NULL))
aff_error("WriteInLogFile");
CloseHandle(hFile);
}
}
Do someone know where the issue comes from ?
Thanks ;)
Even though you're opening the existing file you're not specifying that you want to append to it. Hence it opens as a generic write and you end up overwriting the contents. You need to pass the FILE_APPEND_DATA flag to the CreateFile method. This is best done by using the FILE_GENERIC_WRITE flag which includes FILE_APPEND_DATA
if ((hFile = CreateFile(LOG_FILE_PATH, FILE_GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE
&& (GetLastError() == ERROR_FILE_NOT_FOUND))
{
When you open a file, the pointer will always be positioned to the beginning of the file. To append, you need to explicitly seek to the end (SetFilePointer(hFile, 0, 0, FILE_END);).
Although it may not be causing your actual problem, I'd replace your current logic trying to use CreateFile with OPEN_EXSTING, then with CREATE_NEW if the first attempt fails. Instead, just pass the OPEN_ALWAYS flag, which pretty much automates that logic -- open an existing file if it exists, and create a new one if it doesn't.
You need to set the file pointer to the end of the file before writing with SetFilePointer. See the MSDN example.
I couldn't see anything obvious about opening for Append in the CreateFile documentation, but you could use the SetFilePointer
function to seek to the end of the file before writing.

Resources