[SOLVED] Java File.setlastmodified() fails on Linux when you are not the file owner
Linux - GeneralThis Linux forum is for general Linux questions and discussion.
If it is Linux Related and doesn't seem to fit in any other forum then this is the place.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
Java File.setlastmodified() fails on Linux when you are not the file owner
I have a Raspberry Pi with an external USB drive formatted with an Ext4 file system mounted at boot time via /etc/fstab using:
Code:
/dev/sdb1 /mnt/nxx ext4 defaults 0 2
A java jar file which uses File.setlastmodified() to set a specific time stamp for a file on the drive by referring to /mnt/nxx/file.name works as desired when the file is owned by the same UID as the user running the java process. But it fails on the Pi if the logged-in user is any other UID.
As with the Pi, an Ubuntu host on the same network, and which mounts the same disk at boot time via fstab using:
Code:
192.168.x.x:/mnt/nxx /mnt/nxx nfs rw 0 1
can successfully set the time-stamp only if the logged-in user has the same UID/GID as the Pi.
However, a Windows-10 host that connects via Samba to the same disk (mapped as N:\ to \\RASPI\root\mnt\) and running the same java code can successfully set the time-stamp for any file regardless of the file owner as claimed by the Pi.
All files in question have 777 permissions. As expected, any user on the Pi or any other host in my home network can successfully modify the file.
My goal is to be able to mount the disk and share it via whatever method so that both the Windows and Linux hosts on my home network can use the java code to successfully manipulate the time-stamps.
Why can Windows-10 successfully manipulate the time-stamp regardless of the user while Linux can not? Is there a way for me to add/configure options in the various fstab mount directives, in smb.conf, or elsewhere to configure the disk so that Linux will behave the same way Windows does?
Why can Windows-10 successfully manipulate the time-stamp regardless of the user
Because Windows doesn't have an owner, so it cannot tell who owns the file.
That's why virii are so much more effective in Windows, they can change any file, even most system ones. The owner field you see in Linux (on a Windows fs) is a fake, set by the mounting command. See the uid- and gid= options to that mount command.
PS: most of the attributes of a Windows "file" are fake too, as far as I know only the ro/rw one (for all users!) is real. This is controlled by the umask (and fmask/dmask) option to the mount.
Because Windows doesn't have an owner, so it cannot tell who owns the file.
That implies that a Windows Samba client can forcibly write to any file as long as the file is hosted on a Linux server and accessed via Samba. Now, I know virtually nothing about how file security is implemented within Samba. But I would have expected that the file server would have the final say regarding which clients it allows to read/write. So if Windows can successfully update the time-stamp, it's because Linux is allowing it t do so?
Your summary of Windows' implementation of file attributes is correct. As to access control, my understanding is that Windows uses its ACL features to manage file access. Obviously, mapping Windows ACLs into Linux's permissions architecture is impossible to implement coherently in all cases. I guess that's both a blessing and a curse in managing a multi-platform network.
A java jar file which uses File.setlastmodified() to set a specific time stamp for a file on the drive by referring to /mnt/nxx/file.name works as desired when the file is owned by the same UID as the user running the java process. But it fails on the Pi if the logged-in user is any other UID.
This is usually desired.
But -
the file could be set to 666 permissions - then everybody could change it.
The two users could be in the same group, then the file could be set to 660 or 664 - that should also work.
You can take a look at the file's current ownership & permissions:
All files in question have 777 permissions. As expected, any user on the Pi or any other host in my home network can successfully modify the file.
so it's not a permissions issue. Any user can edit the file -- which does update the file's time-stamp.
The issue I'm describing is that I cannot use a java process to set the time-stamp to a specific value unless the java process is running with the UID of the file owner. I've tried the java code running as a process with the same GID as the file owner. But that fails too. Irrespective of access permissions, a java process running on a Linux platform can set a specific time-stamp ONLY if the process UID matches the file owner's UID. Perhaps this is intended behavior, though I can find no documentation to confirm that. The strange behavior is that a Windows host running the same java code and connecting via Samba can successfully update the time-stamp. I'm still perplexed.
^ No, I didn't notice that, sorry.
777 is never a good idea.
Sounds like a java issue to me, and if I'd had to guess I'd say it's a (security) feature, not a bug. See if java can't be told to allow modifying files from other users or some such. This is just a wild guess though, my java knowledge is pretty close to 0.
@ondoho Agree on both points. 777 is never a good idea. I set those perms mostly to rule-out permissions as the cause of the issue. As noted, I concluded it was NOT a permissions problem.
I'm far from being a java expert myself. Being self-taught in java, I know just enough to be dangerous. That said, I too am beginning to conclude it is a java issue as you suggest. Still trying to learn how to configure NetBeans to compile with the appropriate options to let me fully step-into the code and examine all the objects as I step thru line-by-line.
BTW: Further testing has proven that even when the java process is running as root on a Linux host, the time-stamp cannot be modified unless root is the file owner. So it seems truly that permissions are not the issue, and root privilege isn't relevant either. It works on Linux only if the process UID matches the file owner UID.
Update: Found a 10-year-old post here reporting exactly the same problem. Not a single response. So, clearly, no one in the Oracle Community cares about the issue. I have been unable to contact the poster to find out if he/she ever resolved the issue.
Here's a SSCCE - some version of this should have been in your first post.
Code:
import java.io.*;
public class Main
{
public static void main ( String[] Arguments )
throws Exception
{
touchFile("/path/to/usb/file"); // fails silently when running on Linux
touchFile("/path/to/usb/file/via/samba"); // works when running on Windows
touchFile("/path/to/local/file"); // no information provided
}
static void touchFile( String Filename )
{
var TestFile = new File(Filename);
System.out.println(TestFile.lastModified());
TestFile.setLastModified(TestFile.lastModified()-1234);
System.out.println(TestFile.lastModified());
}
}
Running that code on Java 11.0.6 (you never specified which version of Java you're running) shows that call 3 does not change the last modified value, when performed against a file with a chmod 777 and chown nobody:nogroup applied.
Testing against a local file shows that the file being on a USB drive is not relevant to the issue.
Contrary to post #7, running the same command as root does update the value on my machine.
If this was my issue, I would also be testing the behaviour of:
* going via Samba from the Linux machine
* going via NFS from the Linux machine
* modifying the file contents, not just the modified date
The result of those would give more information about where the issue is.
You are correct @boughtonp, I should have included my code in my post. Based on your suggestion, here is the test code I'm playing with now:
Code:
import java.io.*;
import java.nio.file.Files;
import static java.nio.file.LinkOption.NOFOLLOW_LINKS;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
*
* @author Charlie
*/
public class TouchTest {
/**
* @param args the command line arguments
* @throws java.lang.Exception
*/
public static void main(String[] args)
throws Exception
{
System.out.println("OS is: " + System.getProperty("os.name"));
// The target USB disk is an ext4 file system on the specified host
// mounted via fstab using "/dev/sdb1 /mnt/nas ext4 defaults 0 2"
if (System.getProperty("os.name").equals("Linux")) {
// The Linux host fstab mounts it using "192.168.1.165:/mnt/nas /mnt/nas nfs rw 0 1"
touchFile("/mnt/nas/Test1.txt"); // Owned by UID 0
touchFile("/mnt/nas/Test2.txt"); // Owned by UID 1001
touchFile("/mnt/nas/Test3.txt"); // Owned by UID 1000
} else {
// The Windows host connects via Samba as specified below
// RASPI is 192.168.1.165
touchFile("\\\\RASPI\\root\\mnt\\nas\\Test1.txt");
touchFile("\\\\RASPI\\root\\mnt\\nas\\Test2.txt");
touchFile("\\\\RASPI\\root\\mnt\\nas\\Test3.txt");
}
}
static void touchFile( String Filename ) throws Exception
{
Path path = Paths.get(Filename);
// UID only valid on Linux hosts
if (System.getProperty("os.name").equals("Linux")) {
int uid = (int) Files.getAttribute(path, "unix:uid", NOFOLLOW_LINKS);
System.out.println("UID of " + Filename + " is: " + uid);
}
File TestFile = new File(Filename);
System.out.println("File.setLastModified " +
(TestFile.setLastModified(1000000000000L) ? "Succeeded" : "Failed") + " for " + Filename);
}
} // End-Class Touchtest
In order to test on both windows and Linux, I compile it into a jar file which I run from a command window on the desired platform under various user accounts. As noted in the comments, the three test files are owned by three different UIDs. The target USB disk is plugged into a Raspberry Pi and mounted at boot time as noted, then connected to by the clients as noted in the comments.
When run from an Ubuntu client (which mounts the disk as NFS as noted in the comments) as a user with UID 1000, only Test3.txt succeeds. When UID is 1001, only Test2.txt succeeds. When UID is 0, I expected Test1.txt to succeed -- but all three fail when run as root.
When run from a Windows client, all three succeed regardless of the user. None of the user accounts are privileged accounts.
When run from the Raspberry Pi itself, where the disk is mounted as ext4, the results are Stranger still: When run under UID 0 (root) all three updates succeed. when run under UID 1000, only Test3.txt succeeds. UID 1001 on the Pi is an ftp account, so I did not attempt it using that UID.
As noted in earlier posts, since the files all have 777 perms. Thus any user on any client has no issue modifying any of the files, and when they save edits, the time-stamp is successfully updated to current time. But when java attempts to change only the time-stamp, it succeeds for Linux only when the file UID matches the java process UID.
Have not yet tried you suggestion of accessing the files from a Linux client via Samba though, if it behaves the same as Windows, that might solve my problem.
Bottom line: Results are inconsistent, but seem to depend on how the target disk is mounted. Generally when running from a Linux client as other than root, only a process running with file owner's UID can successfully set the time-stamp independent of actually modifying the file, which does not allow me to specify the desired time-stamp.
When UID is 0, I expected Test1.txt to succeed -- but all three fail when run as root.
Unless the volume is exported with the "no_root_squash" option (see "man exports"), uid 0 (root) does NOT have any special rights on an NFS mounted volume (security feature, the root on the client may not be the same person as the one on the server).
Unless the volume is exported with the "no_root_squash" option (see "man exports"), uid 0 (root) does NOT have any special rights on an NFS mounted volume (security feature, the root on the client may not be the same person as the one on the server).
OK, that could explain some of the behavior. But still, I'm mystified that a connection with a Windows client, for which I have implemented no user mapping, would provide more functionality than a Linux client which appears to be relying on UID.
OK, that could explain some of the behavior. But still, I'm mystified that a connection with a Windows client, for which I have implemented no user mapping, would provide more functionality than a Linux client which appears to be relying on UID.
Tou mean: less security as no ownership is checked.
You mean: less security as no ownership is checked.
You are correct. It might be more accurate to say: "It's surprising to see such radically different security implementations across Operating Systems and file system interfaces." Especially surprising in today's cyber-environment where security is (or should be) so important. Perhaps I need to add a "Security" tag to this thread or revise the description entirely and post it on a security-related forum to get a different set of eyes on it?
When the remote HDD is mounted as CIFS, the behavior on the Linux platform is the same as on Windows: the java code can successfully set the time-stamp for ANY file regardless of the file owner as claimed by the Pi.
As a result, I'm going to mark this issue RESOLVED.
I point out that the original issue remains if the disk is mounted by Linux using NFS. And the same issue applies to disks mounted as NTFS or auto, at least in my home network. But by mounting as a Samba share, the issue is avoided.
I want to thank everyone for their help on this issue, especially @boughtonp and @ehartman
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.