599 lines
13 KiB
C
599 lines
13 KiB
C
#include "ncplib.h"
|
|
#include "nwcrypt.h"
|
|
|
|
typedef __u8 byte;
|
|
typedef __u16 word;
|
|
typedef __u32 dword;
|
|
|
|
#ifdef __KERNEL__
|
|
|
|
#define ncp_printf DPRINTK
|
|
|
|
#else
|
|
|
|
#include <sys/ioctl.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#define ncp_printf printf
|
|
|
|
static void
|
|
assert_server_locked(struct ncp_server *server);
|
|
|
|
static void
|
|
assert_server_not_locked(struct ncp_server *server)
|
|
{
|
|
if (server->lock != 0) {
|
|
ncp_printf("ncpfs: server already locked!\n");
|
|
}
|
|
}
|
|
|
|
static void
|
|
ncp_lock_server(struct ncp_server *server)
|
|
{
|
|
assert_server_not_locked(server);
|
|
server->lock = 1;
|
|
}
|
|
|
|
static void
|
|
ncp_unlock_server(struct ncp_server *server)
|
|
{
|
|
assert_server_locked(server);
|
|
server->lock = 0;
|
|
}
|
|
|
|
static int
|
|
ncp_request(struct ncp_server *server, int function) {
|
|
|
|
struct ncp_reply_header *reply
|
|
= (struct ncp_reply_header *)(server->packet);
|
|
struct ncp_ioctl_request request;
|
|
int result;
|
|
|
|
assert_server_locked(server);
|
|
|
|
if (server->has_subfunction != 0) {
|
|
*(word *)(server->packet) = server->current_size - 2;
|
|
}
|
|
|
|
request.function = function;
|
|
request.size = server->current_size;
|
|
request.data = server->packet;
|
|
|
|
if ((result = ioctl(server->mount_fid, NCP_IOC_NCPREQUEST,
|
|
&request)) < 0) {
|
|
return result;
|
|
}
|
|
|
|
server->ncp_reply_size = result - sizeof(struct ncp_reply_header);
|
|
|
|
return reply->completion_code;
|
|
}
|
|
|
|
static inline int
|
|
min(int a, int b) {
|
|
if (a<b)
|
|
return a;
|
|
else
|
|
return b;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
static void
|
|
assert_server_locked(struct ncp_server *server)
|
|
{
|
|
if (server->lock == 0) {
|
|
ncp_printf("ncpfs: server not locked!\n");
|
|
}
|
|
}
|
|
|
|
static void
|
|
ncp_add_byte(struct ncp_server *server, byte x)
|
|
{
|
|
assert_server_locked(server);
|
|
*(byte *)(&(server->packet[server->current_size])) = x;
|
|
server->current_size += 1;
|
|
return;
|
|
}
|
|
|
|
static void
|
|
ncp_add_word(struct ncp_server *server, word x)
|
|
{
|
|
assert_server_locked(server);
|
|
*(word *)(&(server->packet[server->current_size])) = x;
|
|
server->current_size += 2;
|
|
return;
|
|
}
|
|
|
|
static void
|
|
ncp_add_dword(struct ncp_server *server, dword x)
|
|
{
|
|
assert_server_locked(server);
|
|
*(dword *)(&(server->packet[server->current_size])) = x;
|
|
server->current_size += 4;
|
|
return;
|
|
}
|
|
|
|
static void
|
|
ncp_add_mem(struct ncp_server *server, const char *source, int size)
|
|
{
|
|
assert_server_locked(server);
|
|
memcpy(&(server->packet[server->current_size]), source, size);
|
|
server->current_size += size;
|
|
return;
|
|
}
|
|
|
|
static void
|
|
ncp_add_pstring(struct ncp_server *server, const char *s)
|
|
{
|
|
int len = strlen(s);
|
|
assert_server_locked(server);
|
|
if (len > 255) {
|
|
ncp_printf("ncpfs: string too long: %s\n", s);
|
|
len = 255;
|
|
}
|
|
ncp_add_byte(server, len);
|
|
ncp_add_mem(server, s, len);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
ncp_init_request(struct ncp_server *server)
|
|
{
|
|
ncp_lock_server(server);
|
|
|
|
#ifdef __KERNEL__
|
|
server->current_size = sizeof(struct ncp_request_header);
|
|
#else
|
|
server->current_size = 0;
|
|
server->packet = server->ncp_data;
|
|
#endif
|
|
server->has_subfunction = 0;
|
|
}
|
|
|
|
static void
|
|
ncp_init_request_s(struct ncp_server *server, int subfunction)
|
|
{
|
|
ncp_init_request(server);
|
|
ncp_add_word(server, 0); /* preliminary size */
|
|
|
|
ncp_add_byte(server, subfunction);
|
|
|
|
server->has_subfunction = 1;
|
|
}
|
|
|
|
static char *
|
|
ncp_reply_data(struct ncp_server *server, int offset)
|
|
{
|
|
return &(server->packet[sizeof(struct ncp_reply_header) + offset]);
|
|
}
|
|
|
|
static byte
|
|
ncp_reply_byte(struct ncp_server *server, int offset)
|
|
{
|
|
return *(byte *)(ncp_reply_data(server, offset));
|
|
}
|
|
|
|
static word
|
|
ncp_reply_word(struct ncp_server *server, int offset)
|
|
{
|
|
return *(word *)(ncp_reply_data(server, offset));
|
|
}
|
|
|
|
static dword
|
|
ncp_reply_dword(struct ncp_server *server, int offset)
|
|
{
|
|
return *(dword *)(ncp_reply_data(server, offset));
|
|
}
|
|
|
|
int
|
|
ncp_negotiate_buffersize(struct ncp_server *server,
|
|
int size, int *target) {
|
|
|
|
int result;
|
|
|
|
ncp_init_request(server);
|
|
ncp_add_word(server, htons(size));
|
|
|
|
if ((result = ncp_request(server, 33)) < 0) {
|
|
ncp_printf("ncp_request_error: %d\n", result);
|
|
ncp_unlock_server(server);
|
|
return result;
|
|
}
|
|
|
|
*target =min(ntohs(ncp_reply_word(server, 0)), size);
|
|
|
|
ncp_unlock_server(server);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* result is a 8-byte buffer
|
|
*/
|
|
int
|
|
ncp_get_encryption_key(struct ncp_server *server,
|
|
char *target)
|
|
{
|
|
int result;
|
|
|
|
ncp_init_request_s(server, 23);
|
|
|
|
if ((result = ncp_request(server, 23)) != 0) {
|
|
ncp_printf("ncp_request_error: %d\n", result);
|
|
ncp_unlock_server(server);
|
|
return result;
|
|
}
|
|
|
|
if (server->ncp_reply_size < 8) {
|
|
ncp_printf("ncp_reply_size %d < 8\n",
|
|
server->ncp_reply_size);
|
|
ncp_unlock_server(server);
|
|
return result;
|
|
}
|
|
|
|
memcpy(target, ncp_reply_data(server, 0), 8);
|
|
ncp_unlock_server(server);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ncp_get_bindery_object_id(struct ncp_server *server,
|
|
int object_type, char *object_name,
|
|
struct ncp_bindery_object *target)
|
|
{
|
|
int result;
|
|
ncp_init_request_s(server, 53);
|
|
ncp_add_word(server, ntohs(object_type));
|
|
ncp_add_pstring(server, object_name);
|
|
|
|
if ((result = ncp_request(server, 23)) != 0) {
|
|
ncp_printf("ncp_request_error: %d\n", result);
|
|
ncp_unlock_server(server);
|
|
return result;
|
|
}
|
|
|
|
if (server->ncp_reply_size < 54) {
|
|
ncp_printf("ncp_reply_size %d < 54\n",
|
|
server->ncp_reply_size);
|
|
ncp_unlock_server(server);
|
|
return result;
|
|
}
|
|
|
|
target->object_id = ntohl(ncp_reply_dword(server, 0));
|
|
target->object_type = ntohs(ncp_reply_word (server, 4));
|
|
memcpy(target->object_name, ncp_reply_data(server, 6), 48);
|
|
ncp_unlock_server(server);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ncp_login_encrypted(struct ncp_server *server,
|
|
struct ncp_bindery_object *object,
|
|
unsigned char *key,
|
|
unsigned char *passwd)
|
|
{
|
|
dword tmpID = htonl(object->object_id);
|
|
unsigned char buf[128];
|
|
unsigned char encrypted[8];
|
|
int result;
|
|
|
|
shuffle((byte *)&tmpID, passwd, strlen(passwd), buf);
|
|
nw_encrypt(key, buf, encrypted);
|
|
|
|
ncp_init_request_s(server, 24);
|
|
ncp_add_mem(server, encrypted, 8);
|
|
ncp_add_word(server, htons(object->object_type));
|
|
ncp_add_pstring(server, object->object_name);
|
|
|
|
if ((result = ncp_request(server, 23)) != 0) {
|
|
ncp_printf("ncp_request_error: %d\n", result);
|
|
ncp_unlock_server(server);
|
|
return result;
|
|
}
|
|
|
|
ncp_unlock_server(server);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ncp_login_user(struct ncp_server *server,
|
|
unsigned char *username,
|
|
unsigned char *password)
|
|
{
|
|
int result;
|
|
unsigned char ncp_key[8];
|
|
struct ncp_bindery_object user;
|
|
|
|
if ((result = ncp_get_encryption_key(server, ncp_key)) != 0) {
|
|
return result;
|
|
}
|
|
|
|
if ((result = ncp_get_bindery_object_id(server, NCP_BINDERY_USER,
|
|
username, &user)) != 0) {
|
|
return result;
|
|
}
|
|
|
|
if ((result = ncp_login_encrypted(server, &user,
|
|
ncp_key, password)) != 0) {
|
|
return result;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
int
|
|
ncp_get_volume_info_with_number(struct ncp_server *server, int n,
|
|
struct ncp_volume_info *target)
|
|
{
|
|
int result;
|
|
int len;
|
|
|
|
ncp_init_request_s(server, 44);
|
|
ncp_add_byte(server, n);
|
|
|
|
if ((result = ncp_request(server, 22)) != 0) {
|
|
ncp_printf("ncp_request_error: %d\n", result);
|
|
ncp_unlock_server(server);
|
|
return result;
|
|
}
|
|
|
|
target->total_blocks = ncp_reply_dword(server, 0);
|
|
target->free_blocks = ncp_reply_dword(server, 4);
|
|
target->purgeable_blocks = ncp_reply_dword(server, 8);
|
|
target->not_yet_purgeable_blocks = ncp_reply_dword(server, 12);
|
|
target->total_dir_entries = ncp_reply_dword(server, 16);
|
|
target->available_dir_entries = ncp_reply_dword(server, 20);
|
|
target->sectors_per_block = ncp_reply_byte(server, 28);
|
|
|
|
memset(&(target->volume_name), 0, sizeof(target->volume_name));
|
|
|
|
len = ncp_reply_byte(server, 29);
|
|
if (len > NCP_VOLNAME_LEN) {
|
|
ncp_printf("ncpfs: volume name too long: %d\n", len);
|
|
ncp_unlock_server(server);
|
|
return -EIO;
|
|
}
|
|
|
|
memcpy(&(target->volume_name), ncp_reply_data(server, 30), len);
|
|
ncp_unlock_server(server);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ncp_get_volume_number(struct ncp_server *server, const char *name, int *target)
|
|
{
|
|
int result;
|
|
|
|
ncp_init_request_s(server, 5);
|
|
ncp_add_pstring(server, name);
|
|
|
|
if ((result = ncp_request(server, 22)) != 0) {
|
|
ncp_printf("ncp_request_error: %d\n", result);
|
|
ncp_unlock_server(server);
|
|
return result;
|
|
}
|
|
|
|
*target = ncp_reply_byte(server, 0);
|
|
ncp_unlock_server(server);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
ncp_file_search_init(struct ncp_server *server,
|
|
int dir_handle, const char *path,
|
|
struct ncp_filesearch_info *target)
|
|
{
|
|
int result;
|
|
|
|
ncp_init_request(server);
|
|
ncp_add_byte(server, dir_handle);
|
|
ncp_add_pstring(server, path);
|
|
|
|
if ((result = ncp_request(server, 62)) != 0) {
|
|
ncp_printf("ncp_request_error: %d\n", result);
|
|
ncp_unlock_server(server);
|
|
return result;
|
|
}
|
|
|
|
target->volume_number = ncp_reply_byte(server, 0);
|
|
target->directory_id = ntohs(ncp_reply_word(server, 1));
|
|
target->sequence_no = ntohs(ncp_reply_word(server, 3));
|
|
target->access_rights = ncp_reply_byte(server, 5);
|
|
ncp_unlock_server(server);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
ncp_file_search_continue(struct ncp_server *server,
|
|
struct ncp_filesearch_info *fsinfo,
|
|
int attributes, const char *name,
|
|
struct ncp_file_info *target)
|
|
{
|
|
int result;
|
|
|
|
ncp_init_request(server);
|
|
|
|
ncp_add_byte(server, fsinfo->volume_number);
|
|
ncp_add_word(server, htons(fsinfo->directory_id));
|
|
ncp_add_word(server, htons(fsinfo->sequence_no));
|
|
|
|
ncp_add_byte(server, attributes);
|
|
ncp_add_pstring(server, name);
|
|
|
|
if ((result = ncp_request(server, 63)) != 0) {
|
|
ncp_printf("ncp_request_error: %d\n", result);
|
|
ncp_unlock_server(server);
|
|
return result;
|
|
}
|
|
|
|
fsinfo->sequence_no = ntohs(ncp_reply_word(server, 0));
|
|
|
|
memset(&(target->file_name), 0, sizeof(target->file_name));
|
|
memcpy(&(target->file_name), ncp_reply_data(server, 4),
|
|
NCP_MAX_FILENAME);
|
|
|
|
target->file_attributes = ncp_reply_byte(server, 18);
|
|
target->file_mode = ncp_reply_byte(server, 19);
|
|
target->file_length = ntohl(ncp_reply_dword(server, 20));
|
|
target->creation_date = ntohs(ncp_reply_word(server, 24));
|
|
target->access_date = ntohs(ncp_reply_word(server, 26));
|
|
target->update_date = ntohs(ncp_reply_word(server, 28));
|
|
target->update_time = ntohs(ncp_reply_word(server, 30));
|
|
|
|
ncp_unlock_server(server);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ncp_get_finfo(struct ncp_server *server,
|
|
int dir_handle, const char *path, const char *name,
|
|
struct ncp_file_info *target)
|
|
{
|
|
int result;
|
|
|
|
struct ncp_filesearch_info fsinfo;
|
|
|
|
if ((result = ncp_file_search_init(server, dir_handle, path,
|
|
&fsinfo)) != 0) {
|
|
return result;
|
|
}
|
|
|
|
if ((result = ncp_file_search_continue(server, &fsinfo, 0, name,
|
|
target)) == 0) {
|
|
return result;
|
|
}
|
|
|
|
if ((result = ncp_file_search_init(server, dir_handle, path,
|
|
&fsinfo)) != 0) {
|
|
return result;
|
|
}
|
|
|
|
return ncp_file_search_continue(server, &fsinfo, aDIR, name, target);
|
|
}
|
|
|
|
int
|
|
ncp_open_file(struct ncp_server *server,
|
|
int dir_handle, const char *path,
|
|
int attr, int access,
|
|
struct ncp_file_info *target)
|
|
{
|
|
int result;
|
|
|
|
ncp_init_request(server);
|
|
ncp_add_byte(server, dir_handle);
|
|
ncp_add_byte(server, attr);
|
|
ncp_add_byte(server, access);
|
|
ncp_add_pstring(server, path);
|
|
|
|
if ((result = ncp_request(server, 76)) != 0) {
|
|
ncp_printf("ncp_request_error: %d\n", result);
|
|
ncp_unlock_server(server);
|
|
return result;
|
|
}
|
|
|
|
memcpy(&(target->file_id), ncp_reply_data(server, 0),
|
|
NCP_FILE_ID_LEN);
|
|
|
|
memset(&(target->file_name), 0, sizeof(target->file_name));
|
|
memcpy(&(target->file_name), ncp_reply_data(server, 8),
|
|
NCP_MAX_FILENAME);
|
|
|
|
target->file_attributes = ncp_reply_byte(server, 22);
|
|
target->file_mode = ncp_reply_byte(server, 23);
|
|
target->file_length = ntohl(ncp_reply_dword(server, 24));
|
|
target->creation_date = ntohs(ncp_reply_word(server, 28));
|
|
target->access_date = ntohs(ncp_reply_word(server, 30));
|
|
target->update_date = ntohs(ncp_reply_word(server, 32));
|
|
target->update_time = ntohs(ncp_reply_word(server, 34));
|
|
|
|
ncp_unlock_server(server);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ncp_close_file(struct ncp_server *server, const char *file_id)
|
|
{
|
|
int result;
|
|
|
|
ncp_init_request(server);
|
|
ncp_add_byte(server, 0);
|
|
ncp_add_mem(server, file_id, 6);
|
|
|
|
if ((result = ncp_request(server, 66)) != 0) {
|
|
ncp_printf("ncp_request_error: %d\n", result);
|
|
ncp_unlock_server(server);
|
|
return result;
|
|
}
|
|
|
|
ncp_unlock_server(server);
|
|
return 0;
|
|
}
|
|
|
|
#ifndef __KERNEL__
|
|
|
|
int
|
|
ncp_read(struct ncp_server *server, const char *file_id,
|
|
__u32 offset, __u16 to_read,
|
|
char *target, int *bytes_read)
|
|
{
|
|
int result;
|
|
|
|
ncp_init_request(server);
|
|
ncp_add_byte(server, 0);
|
|
ncp_add_mem(server, file_id, 6);
|
|
ncp_add_dword(server, htonl(offset));
|
|
ncp_add_word(server, htons(to_read));
|
|
|
|
if ((result = ncp_request(server, 72)) != 0) {
|
|
ncp_printf("ncp_request_error: %d\n", result);
|
|
ncp_unlock_server(server);
|
|
return result;
|
|
}
|
|
|
|
*bytes_read = ntohs(ncp_reply_word(server, 0));
|
|
|
|
memcpy(target, ncp_reply_data(server, 2), *bytes_read);
|
|
|
|
ncp_unlock_server(server);
|
|
return 0;
|
|
}
|
|
|
|
#else
|
|
|
|
/* We have to read into user space */
|
|
int
|
|
ncp_read(struct ncp_server *server, const char *file_id,
|
|
__u32 offset, __u16 to_read,
|
|
char *target, int *bytes_read)
|
|
{
|
|
int result;
|
|
|
|
ncp_init_request(server);
|
|
ncp_add_byte(server, 0);
|
|
ncp_add_mem(server, file_id, 6);
|
|
ncp_add_dword(server, htonl(offset));
|
|
ncp_add_word(server, htons(to_read));
|
|
|
|
if ((result = ncp_request(server, 72)) != 0) {
|
|
ncp_printf("ncp_request_error: %d\n", result);
|
|
ncp_unlock_server(server);
|
|
return result;
|
|
}
|
|
|
|
*bytes_read = ntohs(ncp_reply_word(server, 0));
|
|
|
|
memcpy_tofs(target, ncp_reply_data(server, 2), *bytes_read);
|
|
|
|
ncp_unlock_server(server);
|
|
return 0;
|
|
}
|
|
|
|
#endif
|