Pull derived UnpackingDirectory out of Directory
Move all package specifics from now abstract Directory to new derived class UnpackingDirectory and adjust the Volume implementation accordingly. This concludes the Node/Directory refactoring. Neither class is aware of packages anymore.
This commit is contained in:
@@ -54,111 +54,6 @@ Directory::VFSUninit()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
mode_t
|
|
||||||
Directory::Mode() const
|
|
||||||
{
|
|
||||||
if (PackageDirectory* packageDirectory = fPackageDirectories.Head())
|
|
||||||
return packageDirectory->Mode();
|
|
||||||
return S_IFDIR | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
uid_t
|
|
||||||
Directory::UserID() const
|
|
||||||
{
|
|
||||||
if (PackageDirectory* packageDirectory = fPackageDirectories.Head())
|
|
||||||
return packageDirectory->UserID();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
gid_t
|
|
||||||
Directory::GroupID() const
|
|
||||||
{
|
|
||||||
if (PackageDirectory* packageDirectory = fPackageDirectories.Head())
|
|
||||||
return packageDirectory->GroupID();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
timespec
|
|
||||||
Directory::ModifiedTime() const
|
|
||||||
{
|
|
||||||
if (PackageDirectory* packageDirectory = fPackageDirectories.Head())
|
|
||||||
return packageDirectory->ModifiedTime();
|
|
||||||
|
|
||||||
timespec time = { 0, 0 };
|
|
||||||
return time;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
off_t
|
|
||||||
Directory::FileSize() const
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Node*
|
|
||||||
Directory::GetNode()
|
|
||||||
{
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
Directory::AddPackageNode(PackageNode* packageNode)
|
|
||||||
{
|
|
||||||
if (!S_ISDIR(packageNode->Mode()))
|
|
||||||
return B_BAD_VALUE;
|
|
||||||
|
|
||||||
PackageDirectory* packageDirectory
|
|
||||||
= dynamic_cast<PackageDirectory*>(packageNode);
|
|
||||||
|
|
||||||
PackageDirectory* other = fPackageDirectories.Head();
|
|
||||||
bool isNewest = other == NULL
|
|
||||||
|| packageDirectory->ModifiedTime() > other->ModifiedTime();
|
|
||||||
|
|
||||||
if (isNewest)
|
|
||||||
fPackageDirectories.Insert(other, packageDirectory);
|
|
||||||
else
|
|
||||||
fPackageDirectories.Add(packageDirectory);
|
|
||||||
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
Directory::RemovePackageNode(PackageNode* packageNode)
|
|
||||||
{
|
|
||||||
bool isNewest = packageNode == fPackageDirectories.Head();
|
|
||||||
fPackageDirectories.Remove(dynamic_cast<PackageDirectory*>(packageNode));
|
|
||||||
|
|
||||||
// when removing the newest node, we need to find the next node (the list
|
|
||||||
// is not sorted)
|
|
||||||
PackageDirectory* newestNode = fPackageDirectories.Head();
|
|
||||||
if (isNewest && newestNode != NULL) {
|
|
||||||
PackageDirectoryList::Iterator it = fPackageDirectories.GetIterator();
|
|
||||||
it.Next();
|
|
||||||
// skip the first one
|
|
||||||
while (PackageDirectory* otherNode = it.Next()) {
|
|
||||||
if (otherNode->ModifiedTime() > newestNode->ModifiedTime())
|
|
||||||
newestNode = otherNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
fPackageDirectories.Remove(newestNode);
|
|
||||||
fPackageDirectories.Insert(fPackageDirectories.Head(), newestNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
PackageNode*
|
|
||||||
Directory::GetPackageNode()
|
|
||||||
{
|
|
||||||
return fPackageDirectories.Head();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
Directory::Read(off_t offset, void* buffer, size_t* bufferSize)
|
Directory::Read(off_t offset, void* buffer, size_t* bufferSize)
|
||||||
{
|
{
|
||||||
@@ -180,23 +75,6 @@ Directory::ReadSymlink(void* buffer, size_t* bufferSize)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
Directory::OpenAttributeDirectory(AttributeDirectoryCookie*& _cookie)
|
|
||||||
{
|
|
||||||
return UnpackingAttributeDirectoryCookie::Open(fPackageDirectories.Head(),
|
|
||||||
_cookie);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
|
||||||
Directory::OpenAttribute(const char* name, int openMode,
|
|
||||||
AttributeCookie*& _cookie)
|
|
||||||
{
|
|
||||||
return UnpackingAttributeCookie::Open(fPackageDirectories.Head(), name,
|
|
||||||
openMode, _cookie);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
Directory::AddChild(Node* node)
|
Directory::AddChild(Node* node)
|
||||||
{
|
{
|
||||||
@@ -243,18 +121,3 @@ Directory::RemoveDirectoryIterator(DirectoryIterator* iterator)
|
|||||||
{
|
{
|
||||||
fIterators.Remove(iterator);
|
fIterators.Remove(iterator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
RootDirectory::RootDirectory(ino_t id, const timespec& modifiedTime)
|
|
||||||
:
|
|
||||||
Directory(id),
|
|
||||||
fModifiedTime(modifiedTime)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
timespec
|
|
||||||
RootDirectory::ModifiedTime() const
|
|
||||||
{
|
|
||||||
return fModifiedTime;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -7,8 +7,6 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "Node.h"
|
#include "Node.h"
|
||||||
#include "PackageDirectory.h"
|
|
||||||
#include "UnpackingNode.h"
|
|
||||||
|
|
||||||
|
|
||||||
struct DirectoryIterator : DoublyLinkedListLinkImpl<DirectoryIterator> {
|
struct DirectoryIterator : DoublyLinkedListLinkImpl<DirectoryIterator> {
|
||||||
@@ -24,7 +22,7 @@ struct DirectoryIterator : DoublyLinkedListLinkImpl<DirectoryIterator> {
|
|||||||
typedef DoublyLinkedList<DirectoryIterator> DirectoryIteratorList;
|
typedef DoublyLinkedList<DirectoryIterator> DirectoryIteratorList;
|
||||||
|
|
||||||
|
|
||||||
class Directory : public Node, public UnpackingNode {
|
class Directory : public Node {
|
||||||
public:
|
public:
|
||||||
Directory(ino_t id);
|
Directory(ino_t id);
|
||||||
virtual ~Directory();
|
virtual ~Directory();
|
||||||
@@ -34,30 +32,11 @@ public:
|
|||||||
virtual status_t VFSInit(dev_t deviceID);
|
virtual status_t VFSInit(dev_t deviceID);
|
||||||
virtual void VFSUninit();
|
virtual void VFSUninit();
|
||||||
|
|
||||||
virtual mode_t Mode() const;
|
|
||||||
virtual uid_t UserID() const;
|
|
||||||
virtual gid_t GroupID() const;
|
|
||||||
virtual timespec ModifiedTime() const;
|
|
||||||
virtual off_t FileSize() const;
|
|
||||||
|
|
||||||
virtual Node* GetNode();
|
|
||||||
|
|
||||||
virtual status_t AddPackageNode(PackageNode* packageNode);
|
|
||||||
virtual void RemovePackageNode(PackageNode* packageNode);
|
|
||||||
|
|
||||||
virtual PackageNode* GetPackageNode();
|
|
||||||
|
|
||||||
virtual status_t Read(off_t offset, void* buffer,
|
virtual status_t Read(off_t offset, void* buffer,
|
||||||
size_t* bufferSize);
|
size_t* bufferSize);
|
||||||
virtual status_t Read(io_request* request);
|
virtual status_t Read(io_request* request);
|
||||||
|
|
||||||
virtual status_t ReadSymlink(void* buffer,
|
virtual status_t ReadSymlink(void* buffer, size_t* bufferSize);
|
||||||
size_t* bufferSize);
|
|
||||||
|
|
||||||
virtual status_t OpenAttributeDirectory(
|
|
||||||
AttributeDirectoryCookie*& _cookie);
|
|
||||||
virtual status_t OpenAttribute(const char* name, int openMode,
|
|
||||||
AttributeCookie*& _cookie);
|
|
||||||
|
|
||||||
void AddChild(Node* node);
|
void AddChild(Node* node);
|
||||||
void RemoveChild(Node* node);
|
void RemoveChild(Node* node);
|
||||||
@@ -74,7 +53,6 @@ public:
|
|||||||
private:
|
private:
|
||||||
NodeNameHashTable fChildTable;
|
NodeNameHashTable fChildTable;
|
||||||
NodeList fChildList;
|
NodeList fChildList;
|
||||||
PackageDirectoryList fPackageDirectories;
|
|
||||||
DirectoryIteratorList fIterators;
|
DirectoryIteratorList fIterators;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -93,16 +71,4 @@ Directory::NextChild(Node* node) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class RootDirectory : public Directory {
|
|
||||||
public:
|
|
||||||
RootDirectory(ino_t id,
|
|
||||||
const timespec& modifiedTime);
|
|
||||||
|
|
||||||
virtual timespec ModifiedTime() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
timespec fModifiedTime;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif // DIRECTORY_H
|
#endif // DIRECTORY_H
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ HAIKU_PACKAGE_FS_SOURCES =
|
|||||||
Resolvable.cpp
|
Resolvable.cpp
|
||||||
UnpackingAttributeCookie.cpp
|
UnpackingAttributeCookie.cpp
|
||||||
UnpackingAttributeDirectoryCookie.cpp
|
UnpackingAttributeDirectoryCookie.cpp
|
||||||
|
UnpackingDirectory.cpp
|
||||||
UnpackingLeafNode.cpp
|
UnpackingLeafNode.cpp
|
||||||
UnpackingNode.cpp
|
UnpackingNode.cpp
|
||||||
Version.cpp
|
Version.cpp
|
||||||
|
|||||||
@@ -0,0 +1,195 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009-2011, Ingo Weinhold, [email protected].
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "UnpackingDirectory.h"
|
||||||
|
|
||||||
|
#include "DebugSupport.h"
|
||||||
|
#include "UnpackingAttributeCookie.h"
|
||||||
|
#include "UnpackingAttributeDirectoryCookie.h"
|
||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - UnpackingDirectory
|
||||||
|
|
||||||
|
|
||||||
|
UnpackingDirectory::UnpackingDirectory(ino_t id)
|
||||||
|
:
|
||||||
|
Directory(id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
UnpackingDirectory::~UnpackingDirectory()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
UnpackingDirectory::Init(Directory* parent, const char* name)
|
||||||
|
{
|
||||||
|
return Directory::Init(parent, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
mode_t
|
||||||
|
UnpackingDirectory::Mode() const
|
||||||
|
{
|
||||||
|
if (PackageDirectory* packageDirectory = fPackageDirectories.Head())
|
||||||
|
return packageDirectory->Mode();
|
||||||
|
return S_IFDIR | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uid_t
|
||||||
|
UnpackingDirectory::UserID() const
|
||||||
|
{
|
||||||
|
if (PackageDirectory* packageDirectory = fPackageDirectories.Head())
|
||||||
|
return packageDirectory->UserID();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
gid_t
|
||||||
|
UnpackingDirectory::GroupID() const
|
||||||
|
{
|
||||||
|
if (PackageDirectory* packageDirectory = fPackageDirectories.Head())
|
||||||
|
return packageDirectory->GroupID();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
timespec
|
||||||
|
UnpackingDirectory::ModifiedTime() const
|
||||||
|
{
|
||||||
|
if (PackageDirectory* packageDirectory = fPackageDirectories.Head())
|
||||||
|
return packageDirectory->ModifiedTime();
|
||||||
|
|
||||||
|
timespec time = { 0, 0 };
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
off_t
|
||||||
|
UnpackingDirectory::FileSize() const
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Node*
|
||||||
|
UnpackingDirectory::GetNode()
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
UnpackingDirectory::AddPackageNode(PackageNode* packageNode)
|
||||||
|
{
|
||||||
|
if (!S_ISDIR(packageNode->Mode()))
|
||||||
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
|
PackageDirectory* packageDirectory
|
||||||
|
= dynamic_cast<PackageDirectory*>(packageNode);
|
||||||
|
|
||||||
|
PackageDirectory* other = fPackageDirectories.Head();
|
||||||
|
bool isNewest = other == NULL
|
||||||
|
|| packageDirectory->ModifiedTime() > other->ModifiedTime();
|
||||||
|
|
||||||
|
if (isNewest)
|
||||||
|
fPackageDirectories.Insert(other, packageDirectory);
|
||||||
|
else
|
||||||
|
fPackageDirectories.Add(packageDirectory);
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
UnpackingDirectory::RemovePackageNode(PackageNode* packageNode)
|
||||||
|
{
|
||||||
|
bool isNewest = packageNode == fPackageDirectories.Head();
|
||||||
|
fPackageDirectories.Remove(dynamic_cast<PackageDirectory*>(packageNode));
|
||||||
|
|
||||||
|
// when removing the newest node, we need to find the next node (the list
|
||||||
|
// is not sorted)
|
||||||
|
PackageDirectory* newestNode = fPackageDirectories.Head();
|
||||||
|
if (isNewest && newestNode != NULL) {
|
||||||
|
PackageDirectoryList::Iterator it = fPackageDirectories.GetIterator();
|
||||||
|
it.Next();
|
||||||
|
// skip the first one
|
||||||
|
while (PackageDirectory* otherNode = it.Next()) {
|
||||||
|
if (otherNode->ModifiedTime() > newestNode->ModifiedTime())
|
||||||
|
newestNode = otherNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
fPackageDirectories.Remove(newestNode);
|
||||||
|
fPackageDirectories.Insert(fPackageDirectories.Head(), newestNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PackageNode*
|
||||||
|
UnpackingDirectory::GetPackageNode()
|
||||||
|
{
|
||||||
|
return fPackageDirectories.Head();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
UnpackingDirectory::Read(off_t offset, void* buffer, size_t* bufferSize)
|
||||||
|
{
|
||||||
|
return B_IS_A_DIRECTORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
UnpackingDirectory::Read(io_request* request)
|
||||||
|
{
|
||||||
|
return B_IS_A_DIRECTORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
UnpackingDirectory::ReadSymlink(void* buffer, size_t* bufferSize)
|
||||||
|
{
|
||||||
|
return B_IS_A_DIRECTORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
UnpackingDirectory::OpenAttributeDirectory(AttributeDirectoryCookie*& _cookie)
|
||||||
|
{
|
||||||
|
return UnpackingAttributeDirectoryCookie::Open(fPackageDirectories.Head(),
|
||||||
|
_cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
UnpackingDirectory::OpenAttribute(const char* name, int openMode,
|
||||||
|
AttributeCookie*& _cookie)
|
||||||
|
{
|
||||||
|
return UnpackingAttributeCookie::Open(fPackageDirectories.Head(), name,
|
||||||
|
openMode, _cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - RootDirectory
|
||||||
|
|
||||||
|
|
||||||
|
RootDirectory::RootDirectory(ino_t id, const timespec& modifiedTime)
|
||||||
|
:
|
||||||
|
UnpackingDirectory(id),
|
||||||
|
fModifiedTime(modifiedTime)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
timespec
|
||||||
|
RootDirectory::ModifiedTime() const
|
||||||
|
{
|
||||||
|
return fModifiedTime;
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009-2011, Ingo Weinhold, [email protected].
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef UNPACKING_DIRECTORY_H
|
||||||
|
#define UNPACKING_DIRECTORY_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "Directory.h"
|
||||||
|
#include "PackageDirectory.h"
|
||||||
|
#include "UnpackingNode.h"
|
||||||
|
|
||||||
|
|
||||||
|
class UnpackingDirectory : public Directory, public UnpackingNode {
|
||||||
|
public:
|
||||||
|
UnpackingDirectory(ino_t id);
|
||||||
|
virtual ~UnpackingDirectory();
|
||||||
|
|
||||||
|
virtual status_t Init(Directory* parent, const char* name);
|
||||||
|
|
||||||
|
virtual mode_t Mode() const;
|
||||||
|
virtual uid_t UserID() const;
|
||||||
|
virtual gid_t GroupID() const;
|
||||||
|
virtual timespec ModifiedTime() const;
|
||||||
|
virtual off_t FileSize() const;
|
||||||
|
|
||||||
|
virtual Node* GetNode();
|
||||||
|
|
||||||
|
virtual status_t AddPackageNode(PackageNode* packageNode);
|
||||||
|
virtual void RemovePackageNode(PackageNode* packageNode);
|
||||||
|
|
||||||
|
virtual PackageNode* GetPackageNode();
|
||||||
|
|
||||||
|
virtual status_t Read(off_t offset, void* buffer,
|
||||||
|
size_t* bufferSize);
|
||||||
|
virtual status_t Read(io_request* request);
|
||||||
|
|
||||||
|
virtual status_t ReadSymlink(void* buffer,
|
||||||
|
size_t* bufferSize);
|
||||||
|
|
||||||
|
virtual status_t OpenAttributeDirectory(
|
||||||
|
AttributeDirectoryCookie*& _cookie);
|
||||||
|
virtual status_t OpenAttribute(const char* name, int openMode,
|
||||||
|
AttributeCookie*& _cookie);
|
||||||
|
|
||||||
|
private:
|
||||||
|
PackageDirectoryList fPackageDirectories;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class RootDirectory : public UnpackingDirectory {
|
||||||
|
public:
|
||||||
|
RootDirectory(ino_t id,
|
||||||
|
const timespec& modifiedTime);
|
||||||
|
|
||||||
|
virtual timespec ModifiedTime() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
timespec fModifiedTime;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // UNPACKING_DIRECTORY_H
|
||||||
@@ -31,7 +31,6 @@
|
|||||||
#include <package/hpkg/PackageReaderImpl.h>
|
#include <package/hpkg/PackageReaderImpl.h>
|
||||||
|
|
||||||
#include "DebugSupport.h"
|
#include "DebugSupport.h"
|
||||||
#include "Directory.h"
|
|
||||||
#include "kernel_interface.h"
|
#include "kernel_interface.h"
|
||||||
#include "PackageDirectory.h"
|
#include "PackageDirectory.h"
|
||||||
#include "PackageFile.h"
|
#include "PackageFile.h"
|
||||||
@@ -39,6 +38,7 @@
|
|||||||
#include "PackageSymlink.h"
|
#include "PackageSymlink.h"
|
||||||
#include "Resolvable.h"
|
#include "Resolvable.h"
|
||||||
#include "UnpackingLeafNode.h"
|
#include "UnpackingLeafNode.h"
|
||||||
|
#include "UnpackingDirectory.h"
|
||||||
#include "Version.h"
|
#include "Version.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -1070,7 +1070,7 @@ Volume::_CreateUnpackingNode(mode_t mode, Directory* parent, const char* name,
|
|||||||
if (S_ISREG(mode) || S_ISLNK(mode))
|
if (S_ISREG(mode) || S_ISLNK(mode))
|
||||||
unpackingNode = new(std::nothrow) UnpackingLeafNode(fNextNodeID++);
|
unpackingNode = new(std::nothrow) UnpackingLeafNode(fNextNodeID++);
|
||||||
else if (S_ISDIR(mode))
|
else if (S_ISDIR(mode))
|
||||||
unpackingNode = new(std::nothrow) Directory(fNextNodeID++);
|
unpackingNode = new(std::nothrow) UnpackingDirectory(fNextNodeID++);
|
||||||
else
|
else
|
||||||
RETURN_ERROR(B_UNSUPPORTED);
|
RETURN_ERROR(B_UNSUPPORTED);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user