How to open a file by id with DELETE access? - filesystems

Using the NT native function NtCreateFile it's possible to open a file by id using the FILE_OPEN_BY_FILE_ID create option. However, in doing so the DELETE access flag appears to be ignored. If I set it the file will open fine but any attempt to delete or rename the file will fail (e.g. by settings FILE_DELETE_ON_CLOSE or using the FILE_RENAME_INFORMATION class with NtSetInformationFile).
Is it impossible to delete a file opened this way? Is there some other way to delete a file by id instead of name?

In addition to RbMm's answer, I found a blog post by Alex Carp, Some Limitations Using Files Opened By ID, that explains the rationale for this.
Unfortunately the semantics for files opened by ID are a bit different from the semantics of the same files if they would have been opened by name. The file name namespace for example allows multiple names for a file (hardlinks) while the ID namespace does not. The different semantics of the different namespaces can make it so that some operations don't make sense.
For example because NTFS allows multiple names for a file if the file is opened by ID and an operation that changes the namespace is attempted, which name should be affected? To make this very clear, if file \Foo\f.txt and file \Bar\b.txt are hardlinks to the same file and I open the file by ID and I try to rename it, which name should change? How about if I try a delete?
In short deleting a file in NTFS' model actually means removing a reference (aka a name) to a file. It's only once all references to it are deleted that, as a side effect, the file itself can be deleted. Much like reference counting in many programming languages.
There could hypothetically be an operation that takes a file ID and deletes all references as well as the file but this would be a very different operation and potentially tricky (e.g. it would need to perform permission checks on all affected file names, wait for all relevant handles to close, prevent new file names referencing the file being deleted, etc). So in that respect it's unsurprising that it doesn't exist.

i look for ntfs-4 source code and view say next code in NtfsSetRenameInfo
//
// Do a quick check that the caller is allowed to do the rename.
// The opener must have opened the main data stream by name and this can't be
// a system file.
//
if (!FlagOn( Ccb->Flags, CCB_FLAG_OPEN_AS_FILE ) ||
(Lcb == NULL) ||
(NtfsSegmentNumber( &Fcb->FileReference ) < FIRST_USER_FILE_NUMBER)) {
DebugTrace( -1, Dbg, ("NtfsSetRenameInfo: Exit -> %08lx\n", STATUS_INVALID_PARAMETER) );
return STATUS_INVALID_PARAMETER;
}
the same situation for FileDispositionInformation and FILE_DELETE_ON_CLOSE option (1)
if (FlagOn( Ccb->Flags, CCB_FLAG_DELETE_ON_CLOSE )) {
if (FlagOn( Ccb->Flags, CCB_FLAG_OPEN_AS_FILE )) {
so ntfs by some reason not allow rename or delete file if CCB_FLAG_OPEN_AS_FILE not set on file. (it not set when file opened by id)

Related

(Lua 5.2) Cannot create file because io.open returns nil file handle

I am trying to create a file in Lua.
The following threads all say the same thing:
https://forum.rainmeter.net/viewtopic.php?t=10024
Create a new file in Lua/LuaFileSystem
Sifteo: how to create a file.txt with LUA script?
Creating new files with Lua I/O functions
They say to use:
local file = io.open("test.txt", "w")
file:write("Hello World")
file:close()
I have implemented this like so:
local ADDRESSES = "capi/addresses.cfg"
local file, err = io.open(ADDRESSES, "w")
local data = "<DATA>"
file:write(data)
file:close()
This, however, results in the error Attempt to index local 'file' (a nil value). This is, of course, because the file does not exist because I am trying to create it. The examples seem to say that the file should be automatically created when I try to write to it, but how can I do that if the handle is nil!?
It is correct that io.open(filename, "w") will create the file if it doesn't already exist.
However, there are at least three prerequisites common to file operations:
You must have sufficient permissions to create the file in the desired path (e.g. write permission in the folder)
All folders along the path must already exist. Lua will not create folders for you.
There must be sufficient space (your file system may not be full)
You are presumably not meeting one of the prerequisites. To find out which, simply wrap your call to io.open with assert:
local file = assert(io.open(ADDRESSES, "w"))
Now if opening/creating the file fails, Lua will throw an error that tells you which prerequisite you failed to meet.
In your case, I would consider it most probable that the capi directory doesn't exist yet. On my Linux system, the corresponding error message is capi/addresses.cfg: No such file or directory.

C Programming: How to create a parent directory and insert files manually?

My goal is to, inside my C program, make a new directory. Within this directory, I want to create 15 simple text files. The part that I am stuck on is how to generate the 15 text files inside the newly created directory. I have created the new directory like this:
mkdir("new_dir", 0755);
But I am unsure of how to create a text file within it (in the same program). Any tips for this?
I am guessing you are on some POSIX system. The C11 standard (read n1570) does not know about directories (an abstraction provided by your operating system). If you are on Windows, it has a different WinAPI (you should then use CreateDirectory)
First, your call to mkdir(2) could fail (for a variety of reasons, including the fact that the directory did already exist). And very probably, you actually want to create the directory in the home directory, or document that you are creating it in the current working directory (e.g. leave the burden of some appropriate and prior cd shell builtin to your user). Practically speaking, the directory path should be computed at runtime as a string (perhaps using snprintf(3) or asprintf(3)).
So if you wanted to create a directory in the home directory of the user (remember that ~/foo/ is expanded by the shell during globbing, see glob(7)...; you need to fetch the home directory from environ(7)), you would code something like:
char pathbuf[256];
snprintf(pathbuf, sizeof(pathbuf), "%s/new_dir", getenv("HOME"));
to compute that string. Of course, you need to handle failure (of getenv(3), or of snprintf). I am leaving these checks as an exercise. You might want to keep the result of getenv("HOME") in some automatic variable.
Then you need to make the directory, and check against failure. At the very least (using perror(3) and see errno(3)):
if (mkdir (pathbuf, 0750)) { perror(pathbuf); exit(EXIT_FAILURE); }
BTW, the mode passed to mkdir might not allow every other user to write or access it (if it did, you could have some security vulnerability). So I prefer 0750 to yours 0755.
At last you need to create files inside it, perhaps using fopen(3) before writing into them. So some code like
int i = somenumber();
snprintf(pathbuf, sizeof(pathbuf),
"%s/new_dir/file%d.txt", getenv("HOME"), i);
FILE* f = fopen(pathbuf, "w");
if (!f) { perror(pathbuf); exit(EXIT_FAILURE); };
As Jonathan Leffler wisely commented, there are other ways.
My recommendation is to document some convention. Do you want your program to create a directory in the current working directory, or to create it in some "absolute" path, perhaps related to the home directory of your user? If your program is started by some user (and is not setuid or doesn't have root permissions, see credentials(7)) it is not permitted to create directories or files at arbitrary places (see hier(7)).
If on Linux, you'll better read some system programming book like ALP or newer. If you use a different OS, you should read the documentation of its system API.

A file opened for read and write can be unlinked

In my program (on Mac OS X), I opened the file using following code.
int fd;
fd = open(filename, O_RDWR);
Program to delete the file is as follows:
unlink(filename);
In my case, I have same file which is opened and deleted. I observed the following:
After opening the file, I can delete it using this program and even by using rm command.
After deleting the file, read and write operations are working on the file without any problem.
I would like to know the reason behind this. How to prevent rm command or unlink(2) system call from deleting the file which is being opened?
You can't stop unlink(2) from unlinking a file which it has permission to unlink (i.e. it has write access to the directory).
unlink is not called unlink because nobody could think of a better name. It's called that because that is what it does; it unlinks the file from the directory. (A directory is just a collection of links; i.e. it associates names with the location of the corresponding data.) It does not delete the file; the file is garbage collected by the filesystem when there are no longer any links to it.
Open file descriptors are not the only way to keep links to files. Another, quite common, way is to use the link(1) command without the -s option. This creates "hard" links. If a file has several hard links, then removing one of the links (with unlink(2)) does just that -- it removes one of the links.
The rm command has a possibly more confusing name, but it, too, only removes the name, not the file. The file exists as long as someone has a link to it, including a running process.
First, rm command is calling unlink(2)
Then, unlinking an opened file is a normal thing to do on Linux or others Unixes (e.g. MacOSX). It is the canonical way to get temporary files (like tmpfile(3) probably does).
You should understand what inodes are, and realize that a file is not its name or file path, but essentially an inode. A file can have zero, one, or several file paths or names (one can add more with the link(2) syscall, provided all the names sit in the same filesystem). Directory entries associate names to inodes.
So there is no (POSIX-ly portable) way to prohibit I/O on open-ed files without any names.
For some opened file, the kernel has reference counters to its inode, and keep that inode till all processes having open(2)-ed it did close(2) it or have terminated.
See also inode(7) and credentials(7).
It's a normal Situation in UNIX SYSTEM. when you rm or unlink an opened file. UNIX system just mark a flag , and won't really delete the file desception. until the file is closed. and it will be really deleted in the file system.
It's protection to help the daemon work fine.
A link is a name associated to some file (a file is basically unamed). Note that a file could have different names (try ln).
unlink() removes one of this association to a file. If you remove the last link to a file, this just makes you unable to access the file by a name. But, this doesn't mean that the file is unusable, as a file could have been opened and his currently read/written by some application.
A file is removed if and only if :
- there is no link on it
- it is not currently opened by any application

How to get a temporary file name?

I've seen some posts relating to my question, but none that address it completely. I need to create a file in the standard temporary directory and after I'm done writing to it, move it to a different location. The idea is that the file is considered temporary while being downloaded and permanent after downloading completes.
I'm attempting this by calling either mkstemp or tmpfile, then rename after I'm done writing to it. However, I need the full path of the file to call rename, and apparently getting the file name from a file descriptor (returned by mkstemp) or FILE * (returned by tmpfile) is no trivial process. It can be done, but it's not elegant.
Is there a system call that will create a temporary file and provide me with the name? I know about mktemp and related calls, but they either aren't guaranteed to be unique or are deprecated. Or perhaps there is a better way to accomplish creating, writing to, and moving temporary files.
It looks like mkstemp is actually the way to go.
int fd;
char name[] = "/tmp/fileXXXXXX";
fd = mkstemp(name);
/* Check fd. */
After this call you have a valid descriptor in fd and the name of the associated file in name.

Detecting file deletion after fopen

im working in a code that detects changes in a file (a log file) then its process the changes with the help of fseek and ftell. but if the file get deleted and changed (with logrotate) the program stops but not dies, because it not detect more changes (even if the file is recreated). fseek dont show errors and eiter ftell.
how i can detect that file deletion? maybe a way to reopen the file with other FILE *var and comparing file descriptor. but how i can do that. ?
When a file gets deleted, it is not necessarily erased from your disk. In your case the program still has a handle to the old file. The old file handle will not get you any information about its deletion or replacement with another file.
An easy way to detect file deletion and recreation is using stat(2) and fstat(2). They give you a struct stat which contains the inode for the file. When a file is recreated (and still open) the files (old open and recreated) are different and thus the inodes are different. The inode field is st_ino. Yes, you need to poll this unless you wish to use Linux-features like inotify.
You can periodically close the file and open it again, that way you will open the newly created one. Files actually get deleted when there is no handle to the file (open file descriptor is a handle), you are still holding the old file.
On windows, you could set callbacks on the modifications of the FS. Here are details: http://msdn.microsoft.com/en-us/library/aa365261(VS.85).aspx

Resources