]> source.dussan.org Git - nextcloud-server.git/commitdiff
add command for getting fileinfo for debugging
authorRobin Appelman <robin@icewind.nl>
Fri, 31 Mar 2023 16:58:59 +0000 (18:58 +0200)
committerbackportbot-nextcloud[bot] <backportbot-nextcloud[bot]@users.noreply.github.com>
Mon, 1 May 2023 14:03:20 +0000 (14:03 +0000)
Signed-off-by: Robin Appelman <robin@icewind.nl>
apps/files_external/lib/Config/ExternalMountPoint.php
apps/files_sharing/lib/SharedMount.php
core/Command/Info/File.php [new file with mode: 0644]
core/register_command.php
lib/composer/composer/autoload_classmap.php
lib/composer/composer/autoload_static.php

index 090e1c77cdf4b6ca9254b19ebd2326e2b945d590..07475f961f98d150ddf8118aa187dbd73b4d07fa 100644 (file)
@@ -40,4 +40,8 @@ class ExternalMountPoint extends MountPoint {
        public function getMountType() {
                return ($this->storageConfig->getAuthMechanism() instanceof SessionCredentials) ? 'external-session' : 'external';
        }
+
+       public function getStorageConfig(): StorageConfig {
+               return $this->storageConfig;
+       }
 }
index 1e0b2d921e3f2208151601839d28f95b43129fdc..943f14dc2bd5dd46cfe2c5f990c92161846b2c4c 100644 (file)
@@ -253,6 +253,13 @@ class SharedMount extends MountPoint implements MoveableMount {
                return $this->superShare;
        }
 
+       /**
+        * @return \OCP\Share\IShare[]
+        */
+       public function getGroupedShares(): array {
+               return $this->groupedShares;
+       }
+
        /**
         * Get the file id of the root of the storage
         *
diff --git a/core/Command/Info/File.php b/core/Command/Info/File.php
new file mode 100644 (file)
index 0000000..15aa885
--- /dev/null
@@ -0,0 +1,249 @@
+<?php
+
+declare(strict_types=1);
+
+namespace OC\Core\Command\Info;
+
+use OC\Files\ObjectStore\ObjectStoreStorage;
+use OCA\Circles\MountManager\CircleMount;
+use OCA\Files_External\Config\ExternalMountPoint;
+use OCA\Files_Sharing\SharedMount;
+use OCA\GroupFolders\Mount\GroupMountPoint;
+use OCP\Constants;
+use OCP\Files\Config\IUserMountCache;
+use OCP\Files\FileInfo;
+use OCP\Files\Folder;
+use OCP\Files\IHomeStorage;
+use OCP\Files\IRootFolder;
+use OCP\Files\Mount\IMountPoint;
+use OCP\Files\Node;
+use OCP\Files\NotFoundException;
+use OCP\IL10N;
+use OCP\L10N\IFactory;
+use OCP\Share\IShare;
+use OCP\Util;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class File extends Command {
+       private IRootFolder $rootFolder;
+       private IUserMountCache $userMountCache;
+       private IL10N $l10n;
+
+       public function __construct(IRootFolder $rootFolder, IUserMountCache $userMountCache, IFactory $l10nFactory) {
+               $this->rootFolder = $rootFolder;
+               $this->userMountCache = $userMountCache;
+               $this->l10n = $l10nFactory->get("core");
+               parent::__construct();
+       }
+
+       protected function configure() {
+               $this
+                       ->setName('info:file')
+                       ->setDescription('get information for a file')
+                       ->addArgument('file', InputArgument::REQUIRED, "File id or path")
+                       ->addOption('children', 'c', InputOption::VALUE_NONE, "List children of folders");
+       }
+
+       public function execute(InputInterface $input, OutputInterface $output): int {
+               $fileInput = $input->getArgument('file');
+               $showChildren = $input->getOption('children');
+               $node = $this->getNode($fileInput);
+               if (!$node) {
+                       $output->writeln("<error>file $fileInput not found</error>");
+                       return 1;
+               }
+
+               $output->writeln($node->getName());
+               $output->writeln("  fileid: " . $node->getId());
+               $output->writeln("  mimetype: " . $node->getMimetype());
+               $output->writeln("  modified: " . (string)$this->l10n->l("datetime", $node->getMTime()));
+               $output->writeln("  size: " . Util::humanFileSize($node->getSize()));
+               $output->writeln("  " . ($node->isEncrypted() ? "encrypted" : "not encrypted"));
+               if ($node instanceof Folder) {
+                       $children = $node->getDirectoryListing();
+                       if ($showChildren) {
+                               $output->writeln("  children: " . count($children) . ":");
+                               foreach ($children as $child) {
+                                       $output->writeln("  - " . $child->getName());
+                               }
+                       } else {
+                               $output->writeln("  children: " . count($children) . " (--children to list)");
+                       }
+               }
+               $this->outputStorageDetails($node->getMountPoint(), $node, $output);
+
+               $filesPerUser = $this->getFilesByUser($node);
+               $output->writeln("");
+               $output->writeln("The following users have access to the file");
+               $output->writeln("");
+               foreach ($filesPerUser as $user => $files) {
+                       $output->writeln("$user:");
+                       foreach ($files as $userFile) {
+                               $output->writeln("  " . $userFile->getPath() . ": " . $this->formatPermissions($userFile->getType(), $userFile->getPermissions()));
+                               $mount = $userFile->getMountPoint();
+                               $output->writeln("    " . $this->formatMountType($mount));
+                       }
+               }
+
+               return 0;
+       }
+
+       private function getNode(string $fileInput): ?Node {
+               if (is_numeric($fileInput)) {
+                       $mounts = $this->userMountCache->getMountsForFileId((int)$fileInput);
+                       if (!$mounts) {
+                               return null;
+                       }
+                       $mount = $mounts[0];
+                       $userFolder = $this->rootFolder->getUserFolder($mount->getUser()->getUID());
+                       $nodes = $userFolder->getById((int)$fileInput);
+                       if (!$nodes) {
+                               return null;
+                       }
+                       return $nodes[0];
+               } else {
+                       try {
+                               return $this->rootFolder->get($fileInput);
+                       } catch (NotFoundException $e) {
+                               return null;
+                       }
+               }
+       }
+
+       /**
+        * @param FileInfo $file
+        * @return array<string, Node[]>
+        * @throws \OCP\Files\NotPermittedException
+        * @throws \OC\User\NoUserException
+        */
+       private function getFilesByUser(FileInfo $file): array {
+               $id = $file->getId();
+               if (!$id) {
+                       return [];
+               }
+
+               $mounts = $this->userMountCache->getMountsForFileId($id);
+               $result = [];
+               foreach ($mounts as $mount) {
+                       if (isset($result[$mount->getUser()->getUID()])) {
+                               continue;
+                       }
+
+                       $userFolder = $this->rootFolder->getUserFolder($mount->getUser()->getUID());
+                       $result[$mount->getUser()->getUID()] = $userFolder->getById($id);
+               }
+
+               return $result;
+       }
+
+       private function formatPermissions(string $type, int $permissions): string {
+               if ($permissions == Constants::PERMISSION_ALL || ($type === 'file' && $permissions == (Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE))) {
+                       return "full permissions";
+               }
+
+               $perms = [];
+               $allPerms = [Constants::PERMISSION_READ => "read", Constants::PERMISSION_UPDATE => "update", Constants::PERMISSION_CREATE => "create", Constants::PERMISSION_DELETE => "delete", Constants::PERMISSION_SHARE => "share"];
+               foreach ($allPerms as $perm => $name) {
+                       if (($permissions & $perm) === $perm) {
+                               $perms[] = $name;
+                       }
+               }
+
+               return implode(", ", $perms);
+       }
+
+       private function formatMountType(IMountPoint $mountPoint): string {
+               $storage = $mountPoint->getStorage();
+               if ($storage && $storage->instanceOfStorage(IHomeStorage::class)) {
+                       return "home storage";
+               } elseif ($mountPoint instanceof SharedMount) {
+                       $share = $mountPoint->getShare();
+                       $shares = $mountPoint->getGroupedShares();
+                       $sharedBy = array_map(function (IShare $share) {
+                               $shareType = $this->formatShareType($share);
+                               if ($shareType) {
+                                       return $share->getSharedBy() . " (via " . $shareType . " " . $share->getSharedWith() . ")";
+                               } else {
+                                       return $share->getSharedBy();
+                               }
+                       }, $shares);
+                       $description = "shared by " . implode(', ', $sharedBy);
+                       if ($share->getSharedBy() !== $share->getShareOwner()) {
+                               $description .= " owned by " . $share->getShareOwner();
+                       }
+                       return $description;
+               } elseif ($mountPoint instanceof GroupMountPoint) { /** @psalm-suppress UndefinedClass */
+                       return "groupfolder " . $mountPoint->getFolderId();
+               } elseif ($mountPoint instanceof ExternalMountPoint) {
+                       return "external storage " . $mountPoint->getStorageConfig()->getId();
+               } elseif ($mountPoint instanceof CircleMount) {
+                       return "circle";
+               }
+               return get_class($mountPoint);
+       }
+
+       private function formatShareType(IShare $share): ?string {
+               switch ($share->getShareType()) {
+                       case IShare::TYPE_GROUP:
+                               return "group";
+                       case IShare::TYPE_CIRCLE:
+                               return "circle";
+                       case IShare::TYPE_DECK:
+                               return "deck";
+                       case IShare::TYPE_ROOM:
+                               return "room";
+                       case IShare::TYPE_USER:
+                               return null;
+                       default:
+                               return "Unknown (".$share->getShareType().")";
+               }
+       }
+
+       private function outputStorageDetails(IMountPoint $mountPoint, Node $node, OutputInterface $output): void {
+               $storage = $mountPoint->getStorage();
+               if (!$storage) {
+                       return;
+               }
+               if (!$storage->instanceOfStorage(IHomeStorage::class)) {
+                       $output->writeln("  mounted at: " . $mountPoint->getMountPoint());
+               }
+               if ($storage->instanceOfStorage(ObjectStoreStorage::class)) {
+                       /** @var ObjectStoreStorage $storage */
+                       $objectStoreId = $storage->getObjectStore()->getStorageId();
+                       $parts = explode(':', $objectStoreId);
+                       $bucket = array_pop($parts);
+                       $output->writeln("  bucket: " . $bucket);
+                       if ($node instanceof \OC\Files\Node\File) {
+                               $output->writeln("  object id: " . $storage->getURN($node->getId()));
+                               try {
+                                       $fh = $node->fopen('r');
+                                       if (!$fh) {
+                                               throw new NotFoundException();
+                                       }
+                                       $stat = fstat($fh);
+                                       fclose($fh);
+                                       if ($stat['size'] !== $node->getSize()) {
+                                               $output->writeln("  <error>warning: object had a size of " . $stat['size'] . " but cache entry has a size of " . $node->getSize() . "</error>. This should have been automatically repaired");
+                                       }
+                               } catch (\Exception $e) {
+                                       $output->writeln("  <error>warning: object not found in bucket</error>");
+                               }
+                       }
+               } else {
+                       if (!$storage->file_exists($node->getInternalPath())) {
+                               $output->writeln("  <error>warning: file not found in storage</error>");
+                       }
+               }
+               if ($mountPoint instanceof ExternalMountPoint) {
+                       $storageConfig = $mountPoint->getStorageConfig();
+                       $output->writeln("  external storage id: " . $storageConfig->getId());
+                       $output->writeln("  external type: " . $storageConfig->getBackend()->getText());
+               } elseif ($mountPoint instanceof GroupMountPoint) { /** @psalm-suppress UndefinedClass */
+                       $output->writeln("  groupfolder id: " . $mountPoint->getFolderId());
+               }
+       }
+}
index b6da0a6d44d0138579cc3fa5e146886b6adddb3f..0cd71b3b6df05f888996913e1ef93ca967f76692 100644 (file)
@@ -103,6 +103,8 @@ if (\OC::$server->getConfig()->getSystemValue('installed', false)) {
        $application->add(new OC\Core\Command\Config\System\GetConfig(\OC::$server->getSystemConfig()));
        $application->add(new OC\Core\Command\Config\System\SetConfig(\OC::$server->getSystemConfig()));
 
+       $application->add(\OC::$server->get(OC\Core\Command\Info\File::class));
+
        $application->add(new OC\Core\Command\Db\ConvertType(\OC::$server->getConfig(), new \OC\DB\ConnectionFactory(\OC::$server->getSystemConfig())));
        $application->add(new OC\Core\Command\Db\ConvertMysqlToMB4(\OC::$server->getConfig(), \OC::$server->getDatabaseConnection(), \OC::$server->getURLGenerator(), \OC::$server->get(LoggerInterface::class)));
        $application->add(new OC\Core\Command\Db\ConvertFilecacheBigInt(\OC::$server->get(\OC\DB\Connection::class)));
index 80791f61931c347edfcb7b1696a265a589a90c1d..eb9b5528eb8e4a5c266b570e8f162556521ed863 100644 (file)
@@ -926,6 +926,7 @@ return array(
     'OC\\Core\\Command\\Group\\Info' => $baseDir . '/core/Command/Group/Info.php',
     'OC\\Core\\Command\\Group\\ListCommand' => $baseDir . '/core/Command/Group/ListCommand.php',
     'OC\\Core\\Command\\Group\\RemoveUser' => $baseDir . '/core/Command/Group/RemoveUser.php',
+    'OC\\Core\\Command\\Info\\File' => $baseDir . '/core/Command/Info/File.php',
     'OC\\Core\\Command\\Integrity\\CheckApp' => $baseDir . '/core/Command/Integrity/CheckApp.php',
     'OC\\Core\\Command\\Integrity\\CheckCore' => $baseDir . '/core/Command/Integrity/CheckCore.php',
     'OC\\Core\\Command\\Integrity\\SignApp' => $baseDir . '/core/Command/Integrity/SignApp.php',
index 7248dfddea20b166b94ed8f4d2f6390a72a02c27..229cccc411cc83c56a71a2fc1fe857b771772510 100644 (file)
@@ -959,6 +959,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
         'OC\\Core\\Command\\Group\\Info' => __DIR__ . '/../../..' . '/core/Command/Group/Info.php',
         'OC\\Core\\Command\\Group\\ListCommand' => __DIR__ . '/../../..' . '/core/Command/Group/ListCommand.php',
         'OC\\Core\\Command\\Group\\RemoveUser' => __DIR__ . '/../../..' . '/core/Command/Group/RemoveUser.php',
+        'OC\\Core\\Command\\Info\\File' => __DIR__ . '/../../..' . '/core/Command/Info/File.php',
         'OC\\Core\\Command\\Integrity\\CheckApp' => __DIR__ . '/../../..' . '/core/Command/Integrity/CheckApp.php',
         'OC\\Core\\Command\\Integrity\\CheckCore' => __DIR__ . '/../../..' . '/core/Command/Integrity/CheckCore.php',
         'OC\\Core\\Command\\Integrity\\SignApp' => __DIR__ . '/../../..' . '/core/Command/Integrity/SignApp.php',