[starnix] Implement O_NOFOLLOW for open()

Test: OpenTest.OpenNoFollowSymlink
Bug: 79308
Change-Id: I9e853184405dde3fe2a67157133cbd8e67270f8d
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/560262
Commit-Queue: Adam Barth <abarth@google.com>
Fuchsia-Auto-Submit: Adam Barth <abarth@google.com>
Reviewed-by: Theodore Dubois <tbodt@google.com>
diff --git a/src/proc/bin/starnix/task/task.rs b/src/proc/bin/starnix/task/task.rs
index eae6bb3..64f2c2d 100644
--- a/src/proc/bin/starnix/task/task.rs
+++ b/src/proc/bin/starnix/task/task.rs
@@ -443,15 +443,23 @@
         let (dir, path) = self.resolve_dir_fd(dir_fd, path)?;
         let (parent, basename) = self.fs.lookup_parent(dir, path)?;
 
+        let nofollow = flags.contains(OpenFlags::NOFOLLOW);
         let must_create = flags.contains(OpenFlags::CREAT) && flags.contains(OpenFlags::EXCL);
-        let symlink_mode =
-            if must_create { SymlinkFollowing::Disabled } else { SymlinkFollowing::Enabled };
+
+        let symlink_mode = if nofollow || must_create {
+            SymlinkFollowing::Disabled
+        } else {
+            SymlinkFollowing::Enabled
+        };
 
         let node = match parent.lookup(&self.fs, basename, symlink_mode) {
             Ok(node) => {
                 if must_create {
                     return Err(EEXIST);
                 }
+                if nofollow && node.node.info().mode.is_lnk() {
+                    return Err(ELOOP);
+                }
                 node
             }
             Err(errno) => {
diff --git a/src/proc/tests/android/meta/open_test.cml b/src/proc/tests/android/meta/open_test.cml
index 30749a7..64a078a 100644
--- a/src/proc/tests/android/meta/open_test.cml
+++ b/src/proc/tests/android/meta/open_test.cml
@@ -2,7 +2,7 @@
     include: [ "//src/sys/test_runners/starnix/default.shard.cml" ],
     program: {
         binary: "/data/tests/open_test",
-        args: [ "--gunit_filter=*.OTrunc:*.OTruncAndReadOnlyDir:*.OTruncAndReadOnlyFile:*.MustCreateExisting:*.ReadOnly:*.WriteOnly:*.CreateWithAppend:*.ReadWrite:*.RelPath:*.AbsPath:*.AtRelPath:*.AtAbsPath:*.OpenNoFollowStillFollowsLinksInPath:*.Fault:*.AppendOnly:*.AppendConcurrentWrite:*.Truncate:*.NameTooLong:*.DotsFromRoot:*.SymlinkDirectory:*.CanTruncateReadOnly" ],
+        args: [ "--gunit_filter=*.OTrunc:*.OTruncAndReadOnlyDir:*.OTruncAndReadOnlyFile:*.MustCreateExisting:*.ReadOnly:*.WriteOnly:*.CreateWithAppend:*.ReadWrite:*.RelPath:*.AbsPath:*.AtRelPath:*.AtAbsPath:*.OpenNoFollowSymlink:*.OpenNoFollowStillFollowsLinksInPath:*.Fault:*.AppendOnly:*.AppendConcurrentWrite:*.Truncate:*.NameTooLong:*.DotsFromRoot:*.SymlinkDirectory:*.CanTruncateReadOnly" ],
         mounts: [
             "/:remotefs:root",
             "/data:remotefs:data",