File Operations in Android NDK - c

I am using the Android NDK to make an application primarily in C for performance reasons, but it appears that file operations such as fopen do not work correctly in Android. Whenever I try to use these functions, the application crashes.
How do I create/write to a file with the Android NDK?

Other answers are correct. You can open a file through the NDK using FILE and fopen, but don't forget to place a permission for it.
In the Android manifest place:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

File IO works fine on Android using JNI. Perhaps you are trying to open a file with a bad path and not checking the return code? I modified the hello-jni example to demonstrate that it is indeed possible to open file and write to it. I hope this helps.
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include <string.h>
#include <jni.h>
#include <stdio.h>
/* This is a trivial JNI example where we use a native method
* to return a new VM String. See the corresponding Java source
* file located at:
*
* apps/samples/hello-jni/project/src/com/example/HelloJni/HelloJni.java
*/
jstring
Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
jobject thiz )
{
FILE* file = fopen("/sdcard/hello.txt","w+");
if (file != NULL)
{
fputs("HELLO WORLD!\n", file);
fflush(file);
fclose(file);
}
return (*env)->NewStringUTF(env, "Hello from JNI (with file io)!");
}
Here is the result after running it on my phone (with an SD card):
$ adb -d shell cat /sdcard/hello.txt
HELLO WORLD!

Make sure to use the Java getExternalStorageDirectory() call to get the real path to the sdcard since newer devices don't simply map it to "/sdcard". In that case trying to use a hardcoded location of "/sdcard" will fail.

I can also verify that fopen() works correctly, but not if you're trying to access a file in the application's resources or assets folder. I recommend, to avoid having to reinvent the wheel, that you stick any assets you want shipped with your app in the assets folder, where they'll be packaged up for distribution.
In the assets folder case you need to do one of two things, depending on whether the file was compressed by the packager. Both use the AssetManager methods, and you can get the AssetManager from the context/app. File names are always relative to the assets folder, btw: If your have a file "foo.png" directly in the assets folder, you'd open "foo.png," not something like "assets/foo.png".
If the file wasn't compressed (i.e., it's one of the extensions that doesn't get compressed, like .png), you can get a file descriptor from AssetManager.openFd() and pass it to C++. Then you can use fdopen(dup(fd),"r"); to open the file as a FILE*. Note you must fseek() to the offset, and keep track of the length of the file yourself. You're really getting a file handle to the entire assets package, and your file of interest is only a small part.
If your file is compressed, you need to use the Java streaming reader: AssetManager.open() gives you an InputStream you can use the read the file in. This is a PITA because you can't query (AFAIK) the file size; I run a preprocessing step on my assets folder that generates a list of all the files with their respective sizes so I can know, e.g., how big of a buffer to allocate.
If your file is a resource, you may need to go through the Resource class to access it, though it appears resources are also packed into the same assets package. Resource has an openRawResource() call to get the InputStream and an openRawResourceFd() call to get the file descriptor, as above, though.
Good luck.

I want to add my two cents to the answers here, on top of setting the correct permissions specified in the answer to this question, make sure to give your app permission to access the storage in the OS. The permissions menu could change from phone to phone, an easy way to get to it is by going to the Settings menu and then searching for "Permissions." This will give you a chance to give your app permission to access the storage (ie sdcard directory) from NDK code.

Related

Basic UI Gtk with XML interfaces

I'm testing some GTK+ examples.
At some given function, a reference to some path of a XML file appears in
C code. It explains that the code in C is reading the XML content to
later compile it to be usable from the C code:
static void
example_app_window_class_init (ExampleAppWindowClass *class)
{
gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class),
"/org/gtk/exampleapp/window.ui");
}
I can understand what is happening here, but not how is it reading the source XML? window.ui, in this case. Because the repo has no folder
as they mention (/org/gtk/exampleapp/).
So, in my function I expect to do something like:
static void my_style_window_class_init(MyStyleWindowClass *class) {
gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS(class),
"window.ui");
}
All my XML content is in the same folder as *.c and *.h files. This is a testing decision and have no other meaning.
The _from_resource() part of the function name indicates that the path /org/gtk/exampleapp/window.ui is not a filesystem path, but rather a resource path. Resource paths tie into a feature of GLib called GResource which allows you to embed binary data inside a program or shared library.
You would write an XML file to describe what local files map to what resource paths, and then as part of your build process, you would convert that to a C source file with the glib-compile-resources tool. You then build that C source file into your program. The full details are on the page that I linked in the first paragraph.
(Note that these are not the same as the embedded resources in Windows executables, which use a different technology altogether, but work in similar ways.)
If you want to load something from a file, GLib and GTK+ and other libraries built on them provide a _from_file(), _from_data(), or _from_stream() alternative to the _from_resource() function. _from_file() reads the data from a file directly. _from_data() reads from memory. _from_stream() reads from a GStream, which is an object-oriented I/O endpoint defined by GLib in its GIO module. The function name suffix is optional; it varies.
In the case of gtk_widget_class_set_template_from_resource(), the equivalent provided is gtk_widget_class_set_template(), which follows the _from_data() pattern of reading from memory. The memory is stored in a GBytes object, so you have to read from your local file into the GBytes.
It's an oldie and the question seems answered but I'd like to take a direct approach and place solution - turns out that we can substitute this line
gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/gtksourceview/tests/ui/test-widget.ui");
with this line to make the code work.
if (g_file_get_contents("test-widget.ui", &contents, &len, &err) == FALSE)
g_error("error reading test-widget.ui: %s", err->message);
bytes = g_bytes_new_take(contents, len);
gtk_widget_class_set_template(GTK_WIDGET_CLASS(klass), bytes);

WP8 differences between file manipulation APIs in fileapi.h and stdio.h?

According to Supported Win32 APIs for Windows Phone 8, WP8 does support many file manipulation APIs in fileapi.h like CreateFile2, ReadFile, WriteFile, ...
But when I include <stdio.h> I can use fopen, fread, fwrite, ...
Using both those APIs, I can create and read/write to a text file.
CreateFile2("hello.txt", ...);
fopen("hello.txt", ...);
... means other parameters, which aren't important to this question.
The other thing is that I don't know where that text file resides. Installed location isn't the case, because it is read-only location. The other case is Local folder, but I don't specify any Local folder path.
So what are the differences between those APIs (in fileapi.h and stdio.h) and which location does they act on ?
P/S: I'm doing in the WP Runtime Component
The main difference is the API set these functions use.
<stdio.h> contains the file APIs of the standard C library, <fileapi.h> is the Win32 APIs. There are also C++ APIs (<iostream>) which you could use.
I've found that whatever API you use, you should explicitly set the file location to the Local folder.
Platform::String^ localfolder = Windows::Storage::ApplicationData::Current->LocalFolder->Path;
Platform::String^ myFileName = Platform::String::Concat(localfolder, "\\myfile.txt");
One thing to watch is that Platform::String^ uses wchar_t, not char internally so you need to be a bit careful in specifying the file name.
So, try and find an API that takes wchar_t* for the file name and use that to avoid having to do character set conversion.
E.g.: Use _wfsopen instead of fopen.

Calling fopen on Windows core files returns NULL pointer

I am trying to open a couple different files via their absolute path (determined elsewhere, programmatically), so I can get their SHA1 hash*, some of which are core windows files. fopen() is returning NULL on some (but not all) files when I attempt to open them as follows (normally the filename is gotten via QueryFullProcessImageName but I hardcoded it just in case):
char * filename = "c:\\windows\\system32\\spoolsv.exe";
FILE * currFileRead = fopen(filename, "rb");
if (currFileRead == NULL)
{
printf("Failed to open %s, error %s\n", filename, strerror(errno) );
}
else
{
//hashing code
}
The reported error is 2: "No such file or directory", but obviously they're there. It also only fails for some processes, like spoolsv.exe or winlogon.exe, while svchost.exe and wininint.exe seem to open just fine.
My program has administrative privileges, and I can't figure why some processes would fail while others opened without trouble?
*I'm using a method from LibTomCrypt (http://libtom.org/?page=features) which is open source with a permissive license. The call to sha1_process takes in a hash_state (internal to the library), an unsigned char buffer, and the length of the buffer. I need to read the file with fopen to get the file into memory for hashing.
Because your program is a 32-bit process, when you try to open c:\windows\system32 you actually get c:\windows\syswow64 which does not contain all of the same files.
You can use IsWow64Process to determine whether you are running on a 64-bit system. If you are, you can replace system32 with sysnative in the path to open the actual file, unless you need to support Windows 2003 or Windows XP. Depending on your circumstances, you might need to cope with the possibility that the Windows folder is not c:\windows and/or the possibility that there are other folders named system32.
On the whole it would be more robust to have separate 32-bit and 64-bit versions of your application, or perhaps just the particular part of it that is exhibiting the problem. If you can't leave it up to the user to install the appropriate version, the installer could decide which to install, or you could always install both and have the 32-bit version automatically launch the 64-bit version when running on a 64-bit system.
Having administrative privileges is not always enough, because if the file you want to open is in use and the program that is using it has locked it, then you can't open and read that file.

Opening long file names in Windows using fopen with C

I have trouble here using fopen to open files which have paths longer than the 260 characters supported by Windows natively.
I found out about the prefix \\\\?\\ which I need to put in front of the path to be able to handle the file.
My question is: Is this still valid in combination with fopen? I have still trouble to open the files, but I do not find information about it. My paths look like:
\\\\?\\C:\\Deposit\\Source\\Here_Comes_Now_A_List_Of_Many_Subdirs_And_A_Long_File_Name
I am not able to use the Windows API due to the requirement to write a cross-platform tool.
Just to update with the current state: I'm just quoting https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file#enable-long-paths-in-windows-10-version-1607-and-later:
Starting in Windows 10, version 1607, MAX_PATH limitations have been
removed from common Win32 file and directory functions. However, you
must opt-in to the new behavior. To enable the new long path behavior,
both of the following conditions must be met: ...
And it is:
1. Having <longPathAware>true</longPathAware> in your application manifest (it was not default in my C++ Visual Studio project). My manifest file looks like this:
<assembly>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
<ws2:longPathAware>true</ws2:longPathAware>
</windowsSettings>
</application>
</assembly>
and I have added it into Project Options -> Manifest Tool -> Input and Output -> Additional manifest files in my C++ Visual Studio project.
2. Enabled long paths in Windows (this can be done through registry (in item Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem\LongPathsEnabled by setting to 1) or Microsoft says that in Local Group Policy (item Computer Configuration -> Administrative Templates -> System -> Filesystem -> Enable Win32 long paths) - but this does not work for me).
Having both the above configured, you can simply use FILE *f = fopen("../my-very-long-path/my-file.txt") without any limitations (e.g. relative dirs and / with \ replacement work).
You could work around this limitation in a cross-platform way with conditional compilation:
#ifdef WIN32
myFile = _wfopen( ... )
#else
myFile = fopen( ... )
#endif
I think any non-trivial cross-platform project will have to do this somewhere, or else use a library (like SDL) that does.

Duplicate file descriptor with its own file offset

How can one create a new file descriptor from an existing file descriptor such that the new descriptor does not share the same internal file structure/entry in the file table? Specifically attributes such as file offset (and preferably permissions, sharing and modes) should not be shared between the new and old file descriptors.
Under both Windows and Linux, dup() will duplicate the file descriptor, but both descriptors still point to the same file structure in the process' file table. Any seeking on either descriptor will adjust the position for the other descriptors as well.
Note
I've since received answers for both Windows and Linux and adjusted the question a little too often, which has made it difficult for people to answer. I'll adjust my votes and accept the cleanest answer which covers both Windows and Linux. Apologies to all, I'm still new to the SO paradigm. Thanks for the great answers!
So basically, what you really want is to be given a file descriptor, and basically open the same file over again, to get a separate position, sharing, mode, etc. And you want to do this on Windows (where the "file descriptor" is basically a foreign object, not something used directly by the OS or the run-time library at all.
Amazingly enough, there is a way to do that, at least with MS VC++. All but two steps of it use only the Win32 API so porting to other compilers/libraries should be fairly reasonable (I think most supply versions of those two functions). Those are for converting a Unix-style file descriptor to a native Win32 file handle, and converting a native Win32 file handle back to a Unix-style file descriptor.
Convert file-descriptor to native file handle with _get_osfhandle()
Get a name for the file with GetFileInformationByHandleEx(FILE_NAME_INFO)1
Use CreateFile to open a new handle to that file
Create a file descriptor for that handle with _open_osfhandle()
Et voilĂ , we have a new file descriptor referring to the same file, but with its own permissions, position, etc.
Toward the end of your question, you make it sound like you also want the "permissions", but that doesn't seem to make any real sense -- the permissions attach to the file itself, not to how the file is opened, so opening or reopening the file has no effect on the file's permissions. If you really want to know the, you can get it with GetFileInformationByHandle, but be aware that file permissions in Windows are quite a bit different from the (traditional) file permissions in Unix. Unix has owner/group/world permissions on all files, and most systems also have ACLs (though there's more variation in how they work). Windows either has no permissions at all (e.g., files on FAT or FAT32) or else uses ACLs (e.g., files on NTFS), but nothing that's really equivalent to the traditional owner/group/world permissions most people are accustomed to on Unix.
Perhaps you're using "permissions" to refer to whether the file was open for reading, writing, or both. Getting that is considerably uglier than any of the preceding. The problem is that most of it is in the library, not Win32, so there's probably no way to do it that will be even close to portable between compilers. With MS VC++ 9.0 SP1 (not guaranteed for any other compiler) you can do this:
#include <stdio.h>
int get_perms(int fd) {
int i;
FILE * base = __iob_func();
for (i=0; i<_IOB_ENTRIES; i++)
if (base[i]._file == fd)
return base[i]._flag; // we've found our file
return 0; // file wasn't found.
}
Since this involved some spelunking, I wrote a quick test to verify that it might actually work:
#ifdef TEST
#include <io.h>
void show_perms(int perms, char const *caption) {
printf("File opened for %s\n", caption);
printf("Read permission = %d\n", (perms & _IOREAD)!=0);
printf("Write permission = %d\n", (perms & _IOWRT)!=0);
}
int main(int argc, char **argv) {
FILE *file1, *file2;
int perms1, perms2;
file1=fopen(argv[1], "w");
perms1 = get_perms(_fileno(file1));
fclose(file1);
file2=fopen(argv[1], "r");
perms2 = get_perms(_fileno(file2));
fclose(file2);
show_perms(perms1, "writing");
show_perms(perms2, "reading");
return 0;
}
#endif
And the results seem to indicate success:
File opened for writing
Read permission = 0
Write permission = 1
File opened for reading
Read permission = 1
Write permission = 0
You can then test that returned flag against _IOREAD, _IOWRT, and _IORW, which are defined in stdio.h. Despite my previous warnings, I should probably point out that I suspect (though I certainly can't guarantee) that this part of the library is fairly stable, so the real chances of major changes are probably fairly minimal.
In the other direction, however, there's basically no chance at all that it'll work with any other library. It could (but certainly isn't guaranteed to) work with the other compilers that use the MS library, such as Intel, MinGW or Comeau using MS VC++ as its back-end. Of those, I'd say the most likely to work would be Comeau, and the least likely MinGW (but that's only a guess; there's a good chance it won't work with any of them).
Requires the redistributable Win32 FileID API Library
So, I recommend reading up on this a little more. The dup() and related functions serve to create a duplicate value in the file descriptor table pointing to the same entry in the open file table. This is intended to have the same offset. If you call open(), you will create a new entry the open file table.
It doesn't make any sense to create a duplicate of a file descriptor and that new file descriptor have a different offset in the open file table (this seems to contradict what the word "duplicate" means).
I'm not sure what your question is actually. I mean, it isn't the same thing as a duplicate. You could read:
/proc/self/fd/[descriptor]
and get the string that was used to open that file descriptor; bear in mind this may provide some pitfalls, some of which you actually noted in your observation of calling open() again.
Maybe you can explain a little more and I can try to update to help.
Why don't you just open the file a second time with open() or CreateFile() on windows? This gives you all freedom of different access rights and separate offset.
This of course has the drawback that you you can not open the file exclusively, but it solves your problem very simply.

Resources