[mono-android] Changing Folder/File permissions in MfA

Jonathan Pryor jonp at xamarin.com
Wed Oct 24 02:35:56 UTC 2012


On Oct 22, 2012, at 4:47 PM, was <skylogdev at btconnect.com> wrote:
> As I'm not familiar with unix permissions, is there a way of changing the permissions for the App.AppName folder?

Yes: with the chmod(2) system call [0, 1]. (Insert rant about how C# actually allows you to do things like this, while it's not easy in Java w/o using JNI).

> I looked at using permission: MODE_WORLD_READABLE and MODE_WORLD_WRITEABLE, but these aren't available in the MfA 4.2.6 Required Permissions list.

1. They're not permissions, they're integer constants for use with e.g. Context.OpenFileOutput().
2. MODE_WORLD_READABLE is mapped to the Android.Content.FileCreationMode.WorldReadable enum value [2].

> I'm also not sure they would fix the problem anyway.

Using FileCerationMode.WorldReadable would workaround the problem: Context.OpenFileOutput() allows you to specify the FileCreationMode, which sets the Unix mode for that specific file (at creation time, iirc):

	using (var o = new StreamWriter(OpenFileOutput("foo.txt", FileCreationMode.WorldReadable)))
		o.WriteLine ("Hello, world!"); 

The file created in that fashion will be in a directory that by default can't be listed:

	$ adb shell ls -l /data/data/Scratch.PublicFiles/files
	opendir failed, Permission denied 

However, if you know the name of the file you can read it:

	$ adb shell cat /data/data/Scratch.PublicFiles/files/foo.txt
	Hello, world!

This may in fact be suitable for your purposes, and has an added "security" benefit (obfuscation) that the names of the files aren't determined easily.

There is another interesting Context method that takes a FileCreationMode: Context.GetDir(string, FileCreationMode) [3]. This will create a new directory, but what's interesting is two things:

1. The directory it creates isn't a child of Context.FilesDir:

	var d = GetDir ("foo", FileCreationMode.WorldReadable | FileCreationMode.WorldWriteable);
	Console.WriteLine ("Created: {0}", d.AbsolutePath);
	// Created: /data/data/Scratch.PublicFiles/app_foo

Notice that the directory name gets an "app_" prefix.

2. The only way (that I've found) to create a file with a specific set of permissions is Context.OpenFileOutput(), and Context.OpenFileOutput() doesn't allow you to specify sub-directories. Consequently the only way to use the above "app_foo" directory is via normal System.IO methods, which creates files with the default process umask, which is no good here:

	// C# code
	var bar = Path.Combine (d.AbsolutePath, "bar.txt");
	using (var o = File.AppendText (bar))
		o.WriteLine ("managed foo/bar.txt contents");

	# verification...
	$ adb shell ls -l /data/data/Scratch.PublicFiles/app_foo
	-rw------- u0_a62   u0_a62         58 2012-10-23 21:50 bar.txt

We can't do anything with that file; the "other" permissions are "---", aka "nothing".

This makes Context.GetDir() + create-file rather worthless unless you subsequently change the file's permissions:

		int p666 = LinuxUtils.Syscall.S_IRUSR | LinuxUtils.Syscall.S_IWUSR | \
			LinuxUtils.Syscall.S_IRGRP | LinuxUtils.Syscall.S_IWGRP | \
			LinuxUtils.Syscall.S_IROTH | LinuxUtils.Syscall.S_IWOTH;
		LinuxUtils.Syscall.chmod (bar, p666);

Given that the above is (1) ugly, and (2) requires a bit of support code ([0]), the easiest path is to accept the inability to use `ls`, use Context.OpenFileOutput(), and just "know" which files you can read or write.

 - Jon

[0] 
	namespace LinuxUtils {
		using System.Runtime.InteropServices;

		static class Syscall {

			public const int S_IRWXU = 0x1C0; // 00700
			public const int S_IRUSR = 0x100; // 00400
			public const int S_IWUSR = 0x080; // 00200
			public const int S_IXUSR = 0x040; // 00100

			public const int S_IRWXG = 0x038; // 00070
			public const int S_IRGRP = 0x020; // 00040
			public const int S_IWGRP = 0x010; // 00020
			public const int S_IXGRP = 0x008; // 00010

			public const int S_IRWXO = 0x007; // 00007
			public const int S_IROTH = 0x004; // 00004
			public const int S_IWOTH = 0x002; // 00002
			public const int S_IXOTH = 0x001; // 00001

			[DllImport ("c")]
			public static extern int chmod (string path, int mode);
		}
	}

[1] This will change the permissions of /data/data/@PACKAGE_NAME@/files so that it's world readable and world writable, using [0]:

	LinuxUtils.Syscall.chmod (FilesDir.AbsolutePath, LinuxUtils.Syscall.S_IRWXU | LinuxUtils.Syscall.S_IRWXG | LinuxUtils.Syscall.S_IRWXO); 

[2] http://androidapi.xamarin.com/?link=T:Android.Content.FileCreationMode

[3] http://androidapi.xamarin.com/monodoc.ashx?link=M%3aAndroid.Content.Context.GetDir(System.String%2cAndroid.Content.FileCreationMode)



More information about the Monodroid mailing list