vold: Add support for ISO9660/UDF CD-ROM

This commit is squash of the following commits, with improvements:

	Author: Yumi Yukimura <me.cafebabe@gmail.com>
	Date:   Thu Jul 25 03:38:40 2024 +0800

	    vold: Handle USB CDROM that has both filesystem and partition table

	    * Unlike SATA CDROM, USB CDROM has the same major and minor number,
	      and the same block device name (sdX) as USB Disk.
	    * For SATA CDROM, sgdisk_read() would fail, but for USB CDROM it
	      won't fail if partition table is found.
	    * ISO image generated by grub-mkrescue is in such format.

	Author: Chih-Wei Huang <cwhuang@linux.org.tw>
	Date:   Sat Mar 25 16:46:42 2017 +0000

	    vold3: support UDF (Universal Disk Format)

	    Refer to https://en.wikipedia.org/wiki/Universal_Disk_Format.

	    [cafebabe: Apply fixups suggested by Luca Stefani]

	Author: Chih-Wei Huang <cwhuang@linux.org.tw>
	Date:   Sat Mar 25 16:45:41 2017 +0000

	    vold3: auto mount CDROM

	    Luo Chunbo's ISO9660 support (commit 133632d5) is not complete. Still
	    need to handle block devices with major number of CDROM.

	    [cafebabe: Refactor in PublicVolume::doMount()]

	Author: Luo Chunbo <luochunbo@jidemail.com>
	Date:   Wed May 25 16:16:48 2016 +0800

	    vold: ISO9660 support

	    Ref: T7691

	    [cafebabe: Remove MS_DIRSYNC and utf8 flags which are "Invalid argument", Refactor iso9660::Mount() (Suggested edit by Luca Stefani)]

	    Signed-off-by: Luo Chunbo <luochunbo@jidemail.com>

Change-Id: Ideef1064e509cfc5e3bc4cfe9d41cc8149767e6c
diff --git a/Android.bp b/Android.bp
index 9b4de31..d8baf61 100644
--- a/Android.bp
+++ b/Android.bp
@@ -149,6 +149,7 @@
         "fs/Exfat.cpp",
         "fs/Ext4.cpp",
         "fs/F2fs.cpp",
+        "fs/Iso9660.cpp",
         "fs/Ntfs.cpp",
         "fs/Vfat.cpp",
         "model/Disk.cpp",
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index d3c349f..36d2c66 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -104,6 +104,7 @@
 /* 512MiB is large enough for testing purposes */
 static const unsigned int kSizeVirtualDisk = 536870912;
 
+static const unsigned int kMajorBlockCdrom = 11;
 static const unsigned int kMajorBlockMmc = 179;
 
 using ScanProcCallback = bool(*)(uid_t uid, pid_t pid, int nsFd, const char* name, void* params);
@@ -225,6 +226,8 @@
                     int flags = source->getFlags();
                     if (major == kMajorBlockMmc || IsVirtioBlkDevice(major)) {
                         flags |= android::vold::Disk::Flags::kSd;
+                    } else if (major == kMajorBlockCdrom) {
+                        flags |= android::vold::Disk::Flags::kCdrom;
                     } else {
                         flags |= android::vold::Disk::Flags::kUsb;
                     }
diff --git a/fs/Iso9660.cpp b/fs/Iso9660.cpp
new file mode 100644
index 0000000..44f6f21
--- /dev/null
+++ b/fs/Iso9660.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2008 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 <stdio.h>
+#include <sys/mount.h>
+#include <utils/Errors.h>
+#include <android-base/stringprintf.h>
+#include "Iso9660.h"
+#include "Utils.h"
+
+namespace android {
+namespace vold {
+namespace iso9660 {
+
+bool IsIso9660Supported() {
+    return IsFilesystemSupported("iso9660");
+}
+
+bool IsUdfSupported() {
+    return IsFilesystemSupported("udf");
+}
+
+status_t Mount(const std::string& source, const std::string& target,
+        int ownerUid, int ownerGid ) {
+    int mountFlags = MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_RDONLY;
+    auto mountData = android::base::StringPrintf("uid=%d,gid=%d", ownerUid, ownerGid);
+    if (mount(source.c_str(), target.c_str(), "iso9660", mountFlags, mountData.c_str()) == 0) {
+        return 0;
+    }
+    if (mount(source.c_str(), target.c_str(), "udf", mountFlags, mountData.c_str()) == 0) {
+        return 0;
+    }
+    return -1;
+}
+
+}  // namespace iso9660
+}  // namespace vold
+}  // namespace android
diff --git a/fs/Iso9660.h b/fs/Iso9660.h
new file mode 100644
index 0000000..b01000c
--- /dev/null
+++ b/fs/Iso9660.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+#ifndef _ISO9660_H
+#define _ISO9660_H
+
+#include <string>
+
+namespace android {
+namespace vold {
+namespace iso9660 {
+
+bool IsIso9660Supported();
+bool IsUdfSupported();
+status_t Mount(const std::string& source, const std::string& target,
+        int ownerUid, int ownerGid );
+
+}  // namespace iso9660
+}  // namespace vold
+}  // namespace android
+
+
+#endif
diff --git a/main.cpp b/main.cpp
index 1eace14..8956cd5 100644
--- a/main.cpp
+++ b/main.cpp
@@ -76,7 +76,9 @@
                << (android::vold::IsFilesystemSupported("exfat") ? " exfat" : "")
                << (android::vold::IsFilesystemSupported("ext4") ? " ext4" : "")
                << (android::vold::IsFilesystemSupported("f2fs") ? " f2fs" : "")
+               << (android::vold::IsFilesystemSupported("iso9660") ? " iso9660" : "")
                << (android::vold::IsFilesystemSupported("ntfs") ? " ntfs" : "")
+               << (android::vold::IsFilesystemSupported("udf") ? " udf" : "")
                << (android::vold::IsFilesystemSupported("vfat") ? " vfat" : "");
 
     VolumeManager* vm;
diff --git a/model/Disk.cpp b/model/Disk.cpp
index 9576107..1a8a7ac 100644
--- a/model/Disk.cpp
+++ b/model/Disk.cpp
@@ -75,6 +75,7 @@
 static const unsigned int kMajorBlockMmc = 179;
 static const unsigned int kMajorBlockDynamicMin = 234;
 static const unsigned int kMajorBlockDynamicMax = 512;
+static const unsigned int kMajorBlockCdrom = 11;
 
 static const char* kGptBasicData = "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7";
 static const char* kGptLinuxFilesystem = "0FC63DAF-8483-4772-8E79-3D69D8477DE4";
@@ -257,6 +258,9 @@
             mLabel = "Virtual";
             break;
         }
+        case kMajorBlockCdrom:
+            LOG(DEBUG) << "Found a CDROM: " << mSysPath;
+            FALLTHROUGH_INTENDED;
         // clang-format off
         case kMajorBlockScsiA: case kMajorBlockScsiB: case kMajorBlockScsiC:
         case kMajorBlockScsiD: case kMajorBlockScsiE: case kMajorBlockScsiF:
@@ -330,6 +334,12 @@
 }
 
 status_t Disk::readPartitions() {
+    std::vector<std::string> cmd;
+    std::vector<std::string> output;
+    Table table = Table::kUnknown;
+    bool foundParts = false;
+    status_t res;
+
     int maxMinors = getMaxMinors();
     if (maxMinors < 0) {
         return -ENOTSUP;
@@ -343,15 +353,23 @@
 
     destroyAllVolumes();
 
+    if (!maxMinors) {
+        std::string cdFsType, cdUnused;
+        if (ReadMetadataUntrusted(mDevPath, &cdFsType, &cdUnused, &cdUnused) == OK) {
+            if (cdFsType == "iso9660" || cdFsType == "udf") {
+                LOG(INFO) << "Detect " << cdFsType;
+                goto treat_disk_as_partition;
+            }
+        }
+    }
+
     // Parse partition table
 
-    std::vector<std::string> cmd;
     cmd.push_back(kSgdiskPath);
     cmd.push_back("--android-dump");
     cmd.push_back(mDevPath);
 
-    std::vector<std::string> output;
-    status_t res = ForkExecvp(cmd, &output);
+    res = ForkExecvp(cmd, &output);
     if (res != OK) {
         LOG(WARNING) << "sgdisk failed to scan " << mDevPath;
 
@@ -362,8 +380,6 @@
         return res;
     }
 
-    Table table = Table::kUnknown;
-    bool foundParts = false;
     for (const auto& line : output) {
         auto split = android::base::Split(line, kSgdiskToken);
         auto it = split.begin();
@@ -426,6 +442,7 @@
         }
     }
 
+treat_disk_as_partition:
     // Ugly last ditch effort, treat entire disk as partition
     if (table == Table::kUnknown || !foundParts) {
         LOG(WARNING) << mId << " has unknown partition table; trying entire device";
@@ -640,6 +657,9 @@
             // Per Documentation/devices.txt this is static
             return 15;
         }
+        case kMajorBlockCdrom: {
+            return 0;
+        }
         case kMajorBlockMmc: {
             // Per Documentation/devices.txt this is dynamic
             std::string tmp;
diff --git a/model/Disk.h b/model/Disk.h
index 7e90c85..4c78532 100644
--- a/model/Disk.h
+++ b/model/Disk.h
@@ -61,6 +61,8 @@
         kStubVisible = 1 << 6,
         /* Flag that disk is non-removable */
         kNonRemovable = 1 << 7,
+        /* Flag that disk is CDROM */
+        kCdrom = 1 << 8,
     };
 
     const std::string& getId() const { return mId; }
diff --git a/model/PublicVolume.cpp b/model/PublicVolume.cpp
index b4bd43e..bf7aaa6 100644
--- a/model/PublicVolume.cpp
+++ b/model/PublicVolume.cpp
@@ -22,6 +22,7 @@
 #include "fs/Exfat.h"
 #include "fs/Ext4.h"
 #include "fs/F2fs.h"
+#include "fs/Iso9660.h"
 #include "fs/Ntfs.h"
 #include "fs/Vfat.h"
 
@@ -67,6 +68,12 @@
 status_t PublicVolume::readMetadata() {
     status_t res = ReadMetadataUntrusted(mDevPath, &mFsType, &mFsUuid, &mFsLabel);
 
+    // iso9660 has no UUID, we use label as UUID
+    if ((mFsType == "iso9660" || mFsType == "udf") && mFsUuid.empty() && !mFsLabel.empty()) {
+        std::replace(mFsLabel.begin(), mFsLabel.end(), ' ', '_');
+        mFsUuid = mFsLabel;
+    }
+
     auto listener = getListener();
     if (listener) listener->onVolumeMetadataChanged(getId(), mFsType, mFsUuid, mFsLabel);
 
@@ -154,6 +161,8 @@
         ret = ntfs::Check(mDevPath);
     } else if (mFsType == "vfat") {
         ret = vfat::Check(mDevPath);
+    } else if (mFsType == "iso9660" || mFsType == "udf") {
+        // do nothing
     } else {
         LOG(WARNING) << getId() << " unsupported filesystem check, skipping";
     }
@@ -170,6 +179,8 @@
                 false, true);
     } else if (mFsType == "f2fs") {
         ret = f2fs::Mount(mDevPath, mRawPath, mMntOpts, false, true);
+    } else if (mFsType == "iso9660" || mFsType == "udf") {
+        ret = iso9660::Mount(mDevPath, mRawPath, AID_MEDIA_RW, AID_MEDIA_RW);
     } else if (mFsType == "ntfs") {
         ret = ntfs::Mount(mDevPath, mRawPath, AID_ROOT,
                  (isVisible ? AID_MEDIA_RW : AID_EXTERNAL_STORAGE), 0007);