nfs4: Add id mapper

This commit is contained in:
Pawel Dziepak
2012-07-03 21:52:57 +02:00
parent 9b7f2d1b24
commit b2cea80ce5
16 changed files with 670 additions and 18 deletions
+1 -1
View File
@@ -106,7 +106,7 @@ PRIVATE_SYSTEM_LIBS =
SYSTEM_SERVERS = app_server cddb_daemon debug_server input_server mail_daemon
media_addon_server media_server midi_server mount_server net_server
notification_server power_daemon print_server print_addon_server registrar syslog_daemon
dns_resolver_server
dns_resolver_server nfs4_idmapper_server
;
SYSTEM_NETWORK_DEVICES = ethernet loopback ;
@@ -0,0 +1,190 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, [email protected]
*/
#include "IdMap.h"
#include <FindDirectory.h>
#include <team.h>
#include <util/AutoLock.h>
#include "idmapper/IdMapper.h"
IdMap* gIdMapper = NULL;
IdMap::IdMap()
{
mutex_init(&fLock, NULL);
_Repair();
}
IdMap::~IdMap()
{
delete_port(fRequestPort);
delete_port(fReplyPort);
mutex_destroy(&fLock);
}
uid_t
IdMap::GetUserId(const char* owner)
{
return _GetValue<uid_t>(owner, MsgNameToUID);
}
gid_t
IdMap::GetGroupId(const char* ownerGroup)
{
return _GetValue<gid_t>(ownerGroup, MsgNameToGID);
}
char*
IdMap::GetOwner(uid_t user)
{
return reinterpret_cast<char*>(_GetBuffer(user, MsgUIDToName));
}
char*
IdMap::GetOwnerGroup(gid_t group)
{
return reinterpret_cast<char*>(_GetBuffer(group, MsgGIDToName));
}
template<typename T>
T
IdMap::_GetValue(const char* buffer, int32 code)
{
MutexLocker _(fLock);
do {
status_t result = write_port(fRequestPort, MsgNameToUID, buffer,
strlen(buffer) + 1);
if (result != B_OK) {
if (_Repair() != B_OK)
return 0;
else
continue;
}
int32 code;
T value;
result = read_port(fReplyPort, &code, &value, sizeof(T));
if (result < B_OK) {
if (_Repair() != B_OK)
return 0;
else
continue;
}
if (code != MsgReply)
return 0;
else
return value;
} while (true);
}
template<typename T>
void*
IdMap::_GetBuffer(T value, int32 code)
{
MutexLocker _(fLock);
do {
status_t result = write_port(fRequestPort, code, &value, sizeof(value));
if (result != B_OK) {
if (_Repair() != B_OK)
return NULL;
else
continue;
}
ssize_t size = port_buffer_size(fReplyPort);
if (size < B_OK) {
if (_Repair() != B_OK)
return NULL;
else
continue;
}
int32 code;
void* buffer = malloc(size);
if (buffer == NULL)
return NULL;
result = read_port(fReplyPort, &code, buffer, size);
if (result < B_OK) {
free(buffer);
if (_Repair() != B_OK)
return 0;
else
continue;
}
if (code != MsgReply) {
free(buffer);
return NULL;
} else
return buffer;
} while (true);
}
status_t
IdMap::_Repair()
{
status_t result = B_OK;
fRequestPort = create_port(1, kRequestPortName);
if (fRequestPort < B_OK)
return fRequestPort;
fReplyPort = create_port(1, kReplyPortName);
if (fReplyPort < B_OK) {
delete_port(fRequestPort);
return fReplyPort;
}
char path[256];
if (find_directory(B_SYSTEM_SERVERS_DIRECTORY, static_cast<dev_t>(-1),
false, path, sizeof(path)) != B_OK) {
delete_port(fReplyPort);
delete_port(fRequestPort);
return B_NAME_NOT_FOUND;
}
strlcat(path, "/nfs4_idmapper_server", sizeof(path));
const char* args[] = { path, NULL };
thread_id thread = load_image_etc(1, args, NULL, B_NORMAL_PRIORITY,
B_SYSTEM_TEAM, 0);
if (thread < B_OK) {
delete_port(fReplyPort);
delete_port(fRequestPort);
return thread;
}
set_port_owner(fRequestPort, thread);
set_port_owner(fReplyPort, thread);
result = resume_thread(thread);
if (result != B_OK) {
kill_thread(thread);
delete_port(fReplyPort);
delete_port(fRequestPort);
return result;
}
return B_OK;
}
@@ -0,0 +1,47 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, [email protected]
*/
#ifndef IDMAP_H
#define IDMAP_H
#include <lock.h>
#include <port.h>
#include <SupportDefs.h>
class IdMap {
public:
IdMap();
~IdMap();
uid_t GetUserId(const char* owner);
gid_t GetGroupId(const char* ownerGroup);
char* GetOwner(uid_t user);
char* GetOwnerGroup(gid_t group);
private:
status_t _Repair();
template<typename T>
void* _GetBuffer(T value, int32 code);
template<typename T>
T _GetValue(const char* buffer, int32 code);
mutex fLock;
port_id fRequestPort;
port_id fReplyPort;
};
extern IdMap* gIdMapper;
#endif // IDMAP_H
+34 -8
View File
@@ -13,6 +13,7 @@
#include <NodeMonitor.h>
#include "IdMap.h"
#include "Request.h"
#include "RootInode.h"
@@ -345,11 +346,24 @@ Inode::CreateLink(const char* name, const char* path, int mode)
req.PutFH(fInfo.fHandle);
AttrValue attr;
attr.fAttribute = FATTR4_MODE;
attr.fFreePointer = false;
attr.fData.fValue32 = mode;
req.Create(NF4LNK, name, &attr, 1, path);
uint32 i = 0;
AttrValue cattr[3];
cattr[i].fAttribute = FATTR4_MODE;
cattr[i].fFreePointer = false;
cattr[i].fData.fValue32 = mode;
i++;
cattr[i].fAttribute = FATTR4_OWNER;
cattr[i].fFreePointer = true;
cattr[i].fData.fPointer = gIdMapper->GetOwner(getuid());
i++;
cattr[i].fAttribute = FATTR4_OWNER_GROUP;
cattr[i].fFreePointer = true;
cattr[i].fData.fPointer = gIdMapper->GetOwnerGroup(getgid());
i++;
req.Create(NF4LNK, name, cattr, i, path);
status_t result = request.Send();
if (result != B_OK)
@@ -460,6 +474,7 @@ Inode::Stat(struct stat* st)
req.PutFH(fInfo.fHandle);
Attribute attr[] = { FATTR4_SIZE, FATTR4_MODE, FATTR4_NUMLINKS,
FATTR4_OWNER, FATTR4_OWNER_GROUP,
FATTR4_TIME_ACCESS, FATTR4_TIME_CREATE,
FATTR4_TIME_METADATA, FATTR4_TIME_MODIFY };
req.GetAttr(attr, sizeof(attr) / sizeof(Attribute));
@@ -526,6 +541,20 @@ Inode::Stat(struct stat* st)
} else
st->st_nlink = 1;
if (count >= next && values[next].fAttribute == FATTR4_OWNER) {
char* owner = reinterpret_cast<char*>(values[next].fData.fPointer);
st->st_uid = gIdMapper->GetUserId(owner);
next++;
} else
st->st_uid = 0;
if (count >= next && values[next].fAttribute == FATTR4_OWNER_GROUP) {
char* group = reinterpret_cast<char*>(values[next].fData.fPointer);
st->st_gid = gIdMapper->GetGroupId(group);
next++;
} else
st->st_gid = 0;
if (count >= next && values[next].fAttribute == FATTR4_TIME_ACCESS) {
memcpy(&st->st_atim, values[next].fData.fPointer, sizeof(timespec));
next++;
@@ -551,9 +580,6 @@ Inode::Stat(struct stat* st)
} else
memset(&st->st_mtim, 0, sizeof(timespec));
st->st_uid = 0;
st->st_gid = 0;
delete[] values;
return B_OK;
} while (true);
@@ -12,6 +12,7 @@
#include <dirent.h>
#include <string.h>
#include "IdMap.h"
#include "Request.h"
#include "RootInode.h"
@@ -26,11 +27,24 @@ Inode::CreateDir(const char* name, int mode)
req.PutFH(fInfo.fHandle);
AttrValue attr;
attr.fAttribute = FATTR4_MODE;
attr.fFreePointer = false;
attr.fData.fValue32 = mode;
req.Create(NF4DIR, name, &attr, 1);
uint32 i = 0;
AttrValue cattr[3];
cattr[i].fAttribute = FATTR4_MODE;
cattr[i].fFreePointer = false;
cattr[i].fData.fValue32 = mode;
i++;
cattr[i].fAttribute = FATTR4_OWNER;
cattr[i].fFreePointer = true;
cattr[i].fData.fPointer = gIdMapper->GetOwner(getuid());
i++;
cattr[i].fAttribute = FATTR4_OWNER_GROUP;
cattr[i].fFreePointer = true;
cattr[i].fData.fPointer = gIdMapper->GetOwnerGroup(getgid());
i++;
req.Create(NF4DIR, name, cattr, i);
status_t result = request.Send();
if (result != B_OK)
@@ -13,6 +13,7 @@
#include <NodeMonitor.h>
#include "IdMap.h"
#include "Request.h"
@@ -77,7 +78,7 @@ Inode::Create(const char* name, int mode, int perms, OpenFileCookie* cookie,
req.PutFH(fInfo.fHandle);
AttrValue cattr[2];
AttrValue cattr[4];
uint32 i = 0;
if ((mode & O_TRUNC) == O_TRUNC) {
cattr[i].fAttribute = FATTR4_SIZE;
@@ -88,9 +89,21 @@ Inode::Create(const char* name, int mode, int perms, OpenFileCookie* cookie,
cattr[i].fAttribute = FATTR4_MODE;
cattr[i].fFreePointer = false;
cattr[i].fData.fValue32 = perms;
i++;
cattr[i].fAttribute = FATTR4_OWNER;
cattr[i].fFreePointer = true;
cattr[i].fData.fPointer = gIdMapper->GetOwner(getuid());
i++;
cattr[i].fAttribute = FATTR4_OWNER_GROUP;
cattr[i].fFreePointer = true;
cattr[i].fData.fPointer = gIdMapper->GetOwnerGroup(getgid());
i++;
req.Open(CLAIM_NULL, cookie->fSequence++, sModeToAccess(mode),
cookie->fClientId, OPEN4_CREATE, cookie->fOwnerId, name, cattr,
i + 1, (mode & O_EXCL) == O_EXCL);
i, (mode & O_EXCL) == O_EXCL);
req.GetFH();
@@ -8,6 +8,7 @@ KernelAddon nfs4 :
Connection.cpp
FileInfo.cpp
Filesystem.cpp
IdMap.cpp
Inode.cpp
InodeDir.cpp
InodeRegular.cpp
@@ -23,3 +24,6 @@ KernelAddon nfs4 :
RPCServer.cpp
XDR.cpp
;
SubInclude HAIKU_TOP src add-ons kernel file_systems nfs4 idmapper ;
@@ -614,6 +614,20 @@ ReplyInterpreter::_DecodeAttrs(XDR::ReadStream& str, AttrValue** attrs,
current++;
}
if (sIsAttrSet(FATTR4_OWNER, bitmap, bcount)) {
values[current].fAttribute = FATTR4_OWNER;
values[current].fFreePointer = true;
values[current].fData.fPointer = stream.GetString();
current++;
}
if (sIsAttrSet(FATTR4_OWNER_GROUP, bitmap, bcount)) {
values[current].fAttribute = FATTR4_OWNER_GROUP;
values[current].fFreePointer = true;
values[current].fData.fPointer = stream.GetString();
current++;
}
if (sIsAttrSet(FATTR4_SPACE_FREE, bitmap, bcount)) {
values[current].fAttribute = FATTR4_SPACE_FREE;
values[current].fData.fValue64 = stream.GetUHyper();
@@ -776,6 +776,16 @@ RequestBuilder::_EncodeAttrs(XDR::WriteStream& stream, AttrValue* attr,
i++;
}
if (i < count && attr[i].fAttribute == FATTR4_OWNER) {
str.AddString(reinterpret_cast<char*>(attr[i].fData.fPointer));
i++;
}
if (i < count && attr[i].fAttribute == FATTR4_OWNER_GROUP) {
str.AddString(reinterpret_cast<char*>(attr[i].fData.fPointer));
i++;
}
if (i < count && attr[i].fAttribute == FATTR4_TIME_ACCESS_SET) {
str.AddInt(1); // SET_TO_CLIENT_TIME4
+1 -1
View File
@@ -115,7 +115,7 @@ ReadStream::GetUHyper()
}
const char*
char*
ReadStream::GetString()
{
if (_PositionToSize() >= fSize) {
+1 -1
View File
@@ -48,7 +48,7 @@ public:
inline bool GetBoolean();
const char* GetString();
char* GetString();
const void* GetOpaque(uint32* size);
@@ -0,0 +1,266 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#include "IdMapper.h"
#include <grp.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <File.h>
#include <FindDirectory.h>
#include <OS.h>
#include <Path.h>
port_id gRequestPort;
port_id gReplyPort;
const char* kNobodyName = "nobody";
uid_t gNobodyId;
const char* kNogroupName = "nobody";
uid_t gNogroupId;
const char* gDomainName = "localdomain";
status_t
SendError(status_t error)
{
return write_port(gReplyPort, MsgError, &error, sizeof(error));
}
status_t
MatchDomain(char* name)
{
char* domain = strchr(name, '@');
if (domain == NULL)
return B_MISMATCHED_VALUES;
if (strcmp(domain + 1, gDomainName) != 0)
return B_BAD_VALUE;
*domain = '\0';
return B_OK;
}
char*
AddDomain(const char* name)
{
uint32 fullLength = strlen(name) + strlen(gDomainName) + 2;
char* fullName = reinterpret_cast<char*>(malloc(fullLength));
strcpy(fullName, name);
strcat(fullName, "@");
strcat(fullName, gDomainName);
return fullName;
}
status_t
NameToUID(void* buffer)
{
char* userName = reinterpret_cast<char*>(buffer);
struct passwd* userInfo = NULL;
if (MatchDomain(userName) == B_OK)
userInfo = getpwnam(userName);
if (userInfo == NULL)
return write_port(gReplyPort, MsgReply, &gNobodyId, sizeof(gNobodyId));
return write_port(gReplyPort, MsgReply, &userInfo->pw_uid, sizeof(uid_t));
}
status_t
UIDToName(void* buffer)
{
uid_t userId = *reinterpret_cast<uid_t*>(buffer);
const char* fullName = kNobodyName;
struct passwd* userInfo = getpwuid(userId);
if (userInfo != NULL) {
const char* name = userInfo->pw_name;
fullName = AddDomain(name);
}
status_t result = write_port(gReplyPort, MsgReply, fullName,
strlen(fullName) + 1);
free(const_cast<char*>(fullName));
return result;
}
status_t
NameToGID(void* buffer)
{
char* groupName = reinterpret_cast<char*>(buffer);
struct group* groupInfo = NULL;
if (MatchDomain(groupName) == B_OK)
groupInfo = getgrnam(groupName);
if (groupInfo == NULL) {
return write_port(gReplyPort, MsgReply, &gNogroupId,
sizeof(gNogroupId));
}
return write_port(gReplyPort, MsgReply, &groupInfo->gr_gid, sizeof(gid_t));
}
status_t
GIDToName(void* buffer)
{
gid_t groupId = *reinterpret_cast<gid_t*>(buffer);
const char* fullName = kNogroupName;
struct group* groupInfo = getgrgid(groupId);
if (groupInfo != NULL) {
const char* name = groupInfo->gr_name;
fullName = AddDomain(name);
}
status_t result = write_port(gReplyPort, MsgReply, fullName,
strlen(fullName) + 1);
free(const_cast<char*>(fullName));
return result;
}
status_t
ParseRequest(int32 code, void* buffer)
{
switch (code) {
case MsgNameToUID:
return NameToUID(buffer);
case MsgUIDToName:
return UIDToName(buffer);
case MsgNameToGID:
return NameToGID(buffer);
case MsgGIDToName:
return GIDToName(buffer);
default:
return SendError(B_BAD_VALUE);
}
}
status_t
MainLoop()
{
do {
ssize_t size = port_buffer_size(gRequestPort);
if (size < B_OK)
return 0;
void* buffer = malloc(size);
if (buffer == NULL)
return B_NO_MEMORY;
int32 code;
status_t result = read_port(gRequestPort, &code, buffer, size);
if (size < B_OK) {
free(buffer);
return 0;
}
result = ParseRequest(code, buffer);
free(buffer);
if (result == B_BAD_PORT_ID)
return 0;
} while (true);
}
status_t
ReadSettings()
{
BPath path;
status_t result = find_directory(B_COMMON_SETTINGS_DIRECTORY, &path);
if (result != B_OK)
return result;
result = path.Append("nfs4_idmapper.conf");
if (result != B_OK)
return result;
BFile file(path.Path(), B_READ_ONLY);
if (file.InitCheck() != B_OK)
return file.InitCheck();
off_t size;
result = file.GetSize(&size);
if (result != B_OK)
return result;
void* buffer = malloc(size);
if (buffer == NULL)
return B_NO_MEMORY;
file.Read(buffer, size);
gDomainName = reinterpret_cast<char*>(buffer);
return B_OK;
}
int
main(int argc, char** argv)
{
gRequestPort = find_port(kRequestPortName);
if (gRequestPort == B_NAME_NOT_FOUND) {
fprintf(stderr, "%s\n", strerror(gRequestPort));
return gRequestPort;
}
gReplyPort = find_port(kReplyPortName);
if (gReplyPort == B_NAME_NOT_FOUND) {
fprintf(stderr, "%s\n", strerror(gReplyPort));
return gReplyPort;
}
ReadSettings();
struct passwd* userInfo = getpwnam(kNobodyName);
if (userInfo != NULL)
gNobodyId = userInfo->pw_uid;
else
gNobodyId = 0;
struct group* groupInfo = getgrnam(kNogroupName);
if (groupInfo != NULL)
gNogroupId = groupInfo->gr_gid;
else
gNogroupId = 0;
return MainLoop();
}
@@ -0,0 +1,26 @@
/*
* Copyright 2012 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Paweł Dziepak, pdziepak@quarnos.org
*/
#ifndef IDMAPPER_H
#define IDMAPPER_H
enum MsgCode {
MsgError,
MsgReply,
MsgNameToUID,
MsgUIDToName,
MsgNameToGID,
MsgGIDToName
};
static const char* kRequestPortName = "nfs4idmap_request";
static const char* kReplyPortName = "nfs4idmap_reply";
#endif // IDMAPPER_H
@@ -0,0 +1,13 @@
SubDir HAIKU_TOP src add-ons kernel file_systems nfs4 idmapper ;
Application nfs4_idmapper_server
:
IdMapper.cpp
:
be
$(TARGET_LIBSUPC++)
:
nfs4_idmapper_server.rdef
;
@@ -0,0 +1,20 @@
/*
* nfs4_idmapper_server.rdef
*/
resource app_signature "application/x-vnd.Haiku-nfs4_idmapper-server";
resource app_flags B_EXCLUSIVE_LAUNCH | B_BACKGROUND_APP;
resource app_version {
major = 1,
middle = 0,
minor = 0,
variety = B_APPV_ALPHA,
internal = 0,
short_info = "nfs4_idmapper_server",
long_info = "nfs4_idmapper_server ©2012 Haiku, Inc"
};
@@ -15,6 +15,7 @@
#include "Connection.h"
#include "Filesystem.h"
#include "IdMap.h"
#include "Inode.h"
#include "NFS4Defs.h"
#include "RequestBuilder.h"
@@ -546,6 +547,13 @@ nfs4_init()
gRPCServerManager = new(std::nothrow) RPC::ServerManager;
if (gRPCServerManager == NULL)
return B_NO_MEMORY;
gIdMapper = new(std::nothrow) IdMap;
if (gIdMapper == NULL) {
delete gRPCServerManager;
return B_NO_MEMORY;
}
return B_OK;
}
@@ -555,6 +563,7 @@ nfs4_uninit()
{
dprintf("NFS4 Uninit\n");
delete gIdMapper;
delete gRPCServerManager;
return B_OK;