Skip to content

Commit 7858d4b

Browse files
committed
add command for getting fileinfo for debugging
Signed-off-by: Robin Appelman <robin@icewind.nl>
1 parent 1ac7a3f commit 7858d4b

6 files changed

Lines changed: 265 additions & 0 deletions

File tree

apps/files_external/lib/Config/ExternalMountPoint.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,8 @@ public function __construct(StorageConfig $storageConfig, $storage, $mountpoint,
4040
public function getMountType() {
4141
return ($this->storageConfig->getAuthMechanism() instanceof SessionCredentials) ? 'external-session' : 'external';
4242
}
43+
44+
public function getStorageConfig(): StorageConfig {
45+
return $this->storageConfig;
46+
}
4347
}

apps/files_sharing/lib/SharedMount.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,13 @@ public function getShare() {
253253
return $this->superShare;
254254
}
255255

256+
/**
257+
* @return \OCP\Share\IShare[]
258+
*/
259+
public function getGroupedShares(): array {
260+
return $this->groupedShares;
261+
}
262+
256263
/**
257264
* Get the file id of the root of the storage
258265
*

core/Command/Info/File.php

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OC\Core\Command\Info;
6+
7+
use OC\Files\ObjectStore\ObjectStoreStorage;
8+
use OCA\Circles\MountManager\CircleMount;
9+
use OCA\Files_External\Config\ExternalMountPoint;
10+
use OCA\Files_Sharing\SharedMount;
11+
use OCA\GroupFolders\Mount\GroupMountPoint;
12+
use OCP\Constants;
13+
use OCP\Files\Config\IUserMountCache;
14+
use OCP\Files\FileInfo;
15+
use OCP\Files\Folder;
16+
use OCP\Files\IHomeStorage;
17+
use OCP\Files\IRootFolder;
18+
use OCP\Files\Mount\IMountPoint;
19+
use OCP\Files\Node;
20+
use OCP\Files\NotFoundException;
21+
use OCP\IL10N;
22+
use OCP\L10N\IFactory;
23+
use OCP\Share\IShare;
24+
use OCP\Util;
25+
use Symfony\Component\Console\Command\Command;
26+
use Symfony\Component\Console\Input\InputArgument;
27+
use Symfony\Component\Console\Input\InputInterface;
28+
use Symfony\Component\Console\Input\InputOption;
29+
use Symfony\Component\Console\Output\OutputInterface;
30+
31+
class File extends Command {
32+
private IRootFolder $rootFolder;
33+
private IUserMountCache $userMountCache;
34+
private IL10N $l10n;
35+
36+
public function __construct(IRootFolder $rootFolder, IUserMountCache $userMountCache, IFactory $l10nFactory) {
37+
$this->rootFolder = $rootFolder;
38+
$this->userMountCache = $userMountCache;
39+
$this->l10n = $l10nFactory->get("files");
40+
parent::__construct();
41+
}
42+
43+
protected function configure() {
44+
$this
45+
->setName('info:file')
46+
->setDescription('get information for a file')
47+
->addArgument('file', InputArgument::REQUIRED, "File id or path")
48+
->addOption('children', 'c', InputOption::VALUE_NONE, "List children of folders");
49+
}
50+
51+
public function execute(InputInterface $input, OutputInterface $output): int {
52+
$fileInput = $input->getArgument('file');
53+
$showChildren = $input->getOption('children');
54+
$node = $this->getNode($fileInput);
55+
if (!$node) {
56+
$output->writeln("<error>file $fileInput not found</error>");
57+
return 1;
58+
}
59+
60+
$output->writeln($node->getName());
61+
$output->writeln(" fileid: " . $node->getId());
62+
$output->writeln(" mimetype: " . $node->getMimetype());
63+
$output->writeln(" modified: " . (string)$this->l10n->l("datetime", $node->getMTime()));
64+
$output->writeln(" size: " . Util::humanFileSize($node->getSize()));
65+
$output->writeln(" " . ($node->isEncrypted() ? "encrypted" : "not encrypted"));
66+
if ($node instanceof Folder) {
67+
$children = $node->getDirectoryListing();
68+
if ($showChildren) {
69+
$output->writeln(" children: " . count($children) . ":");
70+
foreach ($children as $child) {
71+
$output->writeln(" - " . $child->getName());
72+
}
73+
} else {
74+
$output->writeln(" children: " . count($children) . " (--children to list)");
75+
}
76+
}
77+
$this->outputStorageDetails($node->getMountPoint(), $node, $output);
78+
79+
$filesPerUser = $this->getFilesByUser($node);
80+
$output->writeln("");
81+
$output->writeln("The following users have access to the file");
82+
$output->writeln("");
83+
foreach ($filesPerUser as $user => $files) {
84+
$output->writeln("$user:");
85+
foreach ($files as $userFile) {
86+
$output->writeln(" " . $userFile->getPath() . ": " . $this->formatPermissions($userFile->getType(), $userFile->getPermissions()));
87+
$mount = $userFile->getMountPoint();
88+
$output->writeln(" " . $this->formatMountType($mount));
89+
}
90+
}
91+
92+
return 0;
93+
}
94+
95+
private function getNode(string $fileInput): ?Node {
96+
if (is_numeric($fileInput)) {
97+
$mounts = $this->userMountCache->getMountsForFileId((int)$fileInput);
98+
if (!$mounts) {
99+
return null;
100+
}
101+
$mount = $mounts[0];
102+
$userFolder = $this->rootFolder->getUserFolder($mount->getUser()->getUID());
103+
$nodes = $userFolder->getById((int)$fileInput);
104+
if (!$nodes) {
105+
return null;
106+
}
107+
return $nodes[0];
108+
} else {
109+
try {
110+
return $this->rootFolder->get($fileInput);
111+
} catch (NotFoundException $e) {
112+
return null;
113+
}
114+
}
115+
}
116+
117+
/**
118+
* @param FileInfo $file
119+
* @return array<string, Node[]>
120+
* @throws \OCP\Files\NotPermittedException
121+
* @throws \OC\User\NoUserException
122+
*/
123+
private function getFilesByUser(FileInfo $file): array {
124+
$id = $file->getId();
125+
if (!$id) {
126+
return [];
127+
}
128+
129+
$mounts = $this->userMountCache->getMountsForFileId($id);
130+
$result = [];
131+
foreach ($mounts as $mount) {
132+
if (isset($result[$mount->getUser()->getUID()])) {
133+
continue;
134+
}
135+
136+
$userFolder = $this->rootFolder->getUserFolder($mount->getUser()->getUID());
137+
$result[$mount->getUser()->getUID()] = $userFolder->getById($id);
138+
}
139+
140+
return $result;
141+
}
142+
143+
private function formatPermissions(string $type, int $permissions): string {
144+
if ($permissions == Constants::PERMISSION_ALL || ($type === 'file' && $permissions == (Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE))) {
145+
return "full permissions";
146+
}
147+
148+
$perms = [];
149+
$allPerms = [Constants::PERMISSION_READ => "read", Constants::PERMISSION_UPDATE => "update", Constants::PERMISSION_CREATE => "create", Constants::PERMISSION_DELETE => "delete", Constants::PERMISSION_SHARE => "share"];
150+
foreach ($allPerms as $perm => $name) {
151+
if (($permissions & $perm) === $perm) {
152+
$perms[] = $name;
153+
}
154+
}
155+
156+
return implode(", ", $perms);
157+
}
158+
159+
private function formatMountType(IMountPoint $mountPoint): string {
160+
$storage = $mountPoint->getStorage();
161+
if ($storage && $storage->instanceOfStorage(IHomeStorage::class)) {
162+
return "home storage";
163+
} elseif ($mountPoint instanceof SharedMount) {
164+
$share = $mountPoint->getShare();
165+
$shares = $mountPoint->getGroupedShares();
166+
$sharedBy = array_map(function (IShare $share) {
167+
$shareType = $this->formatShareType($share);
168+
if ($shareType) {
169+
return $share->getSharedBy() . " (via " . $shareType . " " . $share->getSharedWith() . ")";
170+
} else {
171+
return $share->getSharedBy();
172+
}
173+
}, $shares);
174+
$description = "shared by " . implode(', ', $sharedBy);
175+
if ($share->getSharedBy() !== $share->getShareOwner()) {
176+
$description .= " owned by " . $share->getShareOwner();
177+
}
178+
return $description;
179+
} elseif ($mountPoint instanceof GroupMountPoint) {
180+
return "groupfolder " . $mountPoint->getFolderId();
181+
} elseif ($mountPoint instanceof ExternalMountPoint) {
182+
return "external storage " . $mountPoint->getStorageConfig()->getId();
183+
} elseif ($mountPoint instanceof CircleMount) {
184+
return "circle";
185+
}
186+
return get_class($mountPoint);
187+
}
188+
189+
private function formatShareType(IShare $share): ?string {
190+
switch ($share->getShareType()) {
191+
case IShare::TYPE_GROUP:
192+
return "group";
193+
case IShare::TYPE_CIRCLE:
194+
return "circle";
195+
case IShare::TYPE_DECK:
196+
return "deck";
197+
case IShare::TYPE_ROOM:
198+
return "room";
199+
case IShare::TYPE_USER:
200+
return null;
201+
default:
202+
return "Unknown (".$share->getShareType().")";
203+
}
204+
}
205+
206+
private function outputStorageDetails(IMountPoint $mountPoint, Node $node, OutputInterface $output): void {
207+
$storage = $mountPoint->getStorage();
208+
if (!$storage) {
209+
return;
210+
}
211+
if (!$storage->instanceOfStorage(IHomeStorage::class)) {
212+
$output->writeln(" mounted at: " . $mountPoint->getMountPoint());
213+
}
214+
if ($storage->instanceOfStorage(ObjectStoreStorage::class)) {
215+
/** @var ObjectStoreStorage $storage */
216+
$objectStoreId = $storage->getObjectStore()->getStorageId();
217+
$parts = explode(':', $objectStoreId);
218+
$bucket = array_pop($parts);
219+
$output->writeln(" bucket: " . $bucket);
220+
if ($node instanceof \OC\Files\Node\File) {
221+
$output->writeln(" object id: " . $storage->getURN($node->getId()));
222+
try {
223+
$fh = $node->fopen('r');
224+
if (!$fh) {
225+
throw new NotFoundException();
226+
}
227+
$stat = fstat($fh);
228+
fclose($fh);
229+
if ($stat['size'] !== $node->getSize()) {
230+
$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");
231+
}
232+
} catch (\Exception $e) {
233+
$output->writeln(" <error>warning: object not found in bucket</error>");
234+
}
235+
}
236+
} else {
237+
if (!$storage->file_exists($node->getInternalPath())) {
238+
$output->writeln(" <error>warning: file not found in storage</error>");
239+
}
240+
}
241+
if ($mountPoint instanceof ExternalMountPoint) {
242+
$storageConfig = $mountPoint->getStorageConfig();
243+
$output->writeln(" external storage id: " . $storageConfig->getId());
244+
$output->writeln(" external type: " . $storageConfig->getBackend()->getText());
245+
/** @psalm-suppress UndefinedClass */
246+
} elseif ($mountPoint instanceof GroupMountPoint) {
247+
$output->writeln(" groupfolder id: " . $mountPoint->getFolderId());
248+
}
249+
}
250+
}

core/register_command.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@
103103
$application->add(new OC\Core\Command\Config\System\GetConfig(\OC::$server->getSystemConfig()));
104104
$application->add(new OC\Core\Command\Config\System\SetConfig(\OC::$server->getSystemConfig()));
105105

106+
$application->add(\OC::$server->get(OC\Core\Command\Info\File::class));
107+
106108
$application->add(new OC\Core\Command\Db\ConvertType(\OC::$server->getConfig(), new \OC\DB\ConnectionFactory(\OC::$server->getSystemConfig())));
107109
$application->add(new OC\Core\Command\Db\ConvertMysqlToMB4(\OC::$server->getConfig(), \OC::$server->getDatabaseConnection(), \OC::$server->getURLGenerator(), \OC::$server->get(LoggerInterface::class)));
108110
$application->add(new OC\Core\Command\Db\ConvertFilecacheBigInt(\OC::$server->get(\OC\DB\Connection::class)));

lib/composer/composer/autoload_classmap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -941,6 +941,7 @@
941941
'OC\\Core\\Command\\Group\\Info' => $baseDir . '/core/Command/Group/Info.php',
942942
'OC\\Core\\Command\\Group\\ListCommand' => $baseDir . '/core/Command/Group/ListCommand.php',
943943
'OC\\Core\\Command\\Group\\RemoveUser' => $baseDir . '/core/Command/Group/RemoveUser.php',
944+
'OC\\Core\\Command\\Info\\File' => $baseDir . '/core/Command/Info/File.php',
944945
'OC\\Core\\Command\\Integrity\\CheckApp' => $baseDir . '/core/Command/Integrity/CheckApp.php',
945946
'OC\\Core\\Command\\Integrity\\CheckCore' => $baseDir . '/core/Command/Integrity/CheckCore.php',
946947
'OC\\Core\\Command\\Integrity\\SignApp' => $baseDir . '/core/Command/Integrity/SignApp.php',

lib/composer/composer/autoload_static.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -974,6 +974,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
974974
'OC\\Core\\Command\\Group\\Info' => __DIR__ . '/../../..' . '/core/Command/Group/Info.php',
975975
'OC\\Core\\Command\\Group\\ListCommand' => __DIR__ . '/../../..' . '/core/Command/Group/ListCommand.php',
976976
'OC\\Core\\Command\\Group\\RemoveUser' => __DIR__ . '/../../..' . '/core/Command/Group/RemoveUser.php',
977+
'OC\\Core\\Command\\Info\\File' => __DIR__ . '/../../..' . '/core/Command/Info/File.php',
977978
'OC\\Core\\Command\\Integrity\\CheckApp' => __DIR__ . '/../../..' . '/core/Command/Integrity/CheckApp.php',
978979
'OC\\Core\\Command\\Integrity\\CheckCore' => __DIR__ . '/../../..' . '/core/Command/Integrity/CheckCore.php',
979980
'OC\\Core\\Command\\Integrity\\SignApp' => __DIR__ . '/../../..' . '/core/Command/Integrity/SignApp.php',

0 commit comments

Comments
 (0)