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

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.

Related

How to open a file by id with DELETE access?

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)

Case Sensitive `os.Stat`

I am writing a linting tool; one of the rules enforces that all linked files have lowercase filenames on disk, ie:
some/asset.png == OK
another/asset.PNG == Fail
I was hoping to validate this rule with the following code:
f, err := os.Stat("another/asset.png")
actualName := f.Name()
However the retun value of f.Name() always reflects the value passed to os.Stat(), eg: passing os.Stat("foo.BAR") will yield foo.BAR even if the file is named foo.BaR on disk.
Am I just approaching this problem wrong? Is there another way to get the actual, case-sensitive filename on disk for a given filepath?
Thanks.
When you stat a file, the name returned in the info is the one you provided, as neither the stat_t nor Win32FileAttributeData data structures contain a name (in POSIX a file itself doesn't really have a name, it's just an inode named via 1 or more hardlinks) The actual name of the file on disk is stored in the parent directory. You will need to iterate through the directory entries to find the name as it's stored on disk.
You can either open the directory file directly and call File.ReadDir, or use the ioutil.ReadDir convenience function.

Pharo FileSystem: setUp of SUnit test which uses a file

I want to write a SUnit test which uses a file with the Pharo 4.0 FileSystem.
I want to write a file and then later read it.
Something like this
fname := 'TabularTestExport1.xlsx'.
(FileLocator temp / fname ) delete.
TabularXSLXExport workbook: myWorkbook fileName: (FileLocator temp / fname ).
Questions
temp directory What is the method to use for using a temporary file in a platform independant way. FileLocator temp or FileLocator tempDirectory is not implemented.
deleting an existing test file How do I ensure that a file is deleted? I.e. How do I avoid a walkback in case the file does not exist.
Alternatively everything could be done in the memory: 1. creation of test file, 2. exporting test file, 3. Importing test file back
For tests, unless you have a real big archive, is better to do things in memory.
FileSystem provides you a way to do it, you just need to do:
fs := FileSystem memory.
It will give you a compatible API so you can make your tests.
If you want a file and not a directory, you can do:
file := FileSystem memory / 'myFile'.
EDIT: I forget a couple of things:
FileLocator temp is implemented and should work fine for you. Why you say is not implemented? Are you not finding it for some reason, maybe?
myFileReference ensureDelete will... well, ensure your file is deleted :)

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.

Resources