mirror of
https://git.code.sf.net/p/ntfs-3g/ntfs-3g.git
synced 2024-11-23 18:14:24 +08:00
3d7a28bfa5
These tools were originally developed for running on Windows and later ported to libntfs-3g. This patch makes them similar to other ntfsprogs tools, dropping the native Windows interfaces and using libntfs-3g on all platforms. There is no change in usage or supported features, only the command names have changed.
1133 lines
26 KiB
C
1133 lines
26 KiB
C
/*
|
|
* Windows to Linux user mapping for ntfs-3g
|
|
*
|
|
*
|
|
* Copyright (c) 2007-2016 Jean-Pierre Andre
|
|
*
|
|
* A quick'n dirty program scanning owners of files in
|
|
* "c:\Documents and Settings" (and "c:\Users")
|
|
* and asking user to map them to Linux accounts
|
|
*
|
|
* History
|
|
*
|
|
* Sep 2007
|
|
* - first version, limited to Win32
|
|
*
|
|
* Oct 2007
|
|
* - ported to Linux (rewritten would be more correct)
|
|
*
|
|
* Nov 2007 Version 1.0.0
|
|
* - added more defaults
|
|
*
|
|
* Nov 2007 Version 1.0.1
|
|
* - avoided examining files whose name begin with a '$'
|
|
*
|
|
* Jan 2008 Version 1.0.2
|
|
* - moved user mapping file to directory .NTFS-3G (hidden for Linux)
|
|
* - fixed an error case in Windows version
|
|
*
|
|
* Nov 2008 Version 1.1.0
|
|
* - fixed recursions for account in Linux version
|
|
* - searched owner in c:\Users (standard location for Vista)
|
|
*
|
|
* May 2009 Version 1.1.1
|
|
* - reordered mapping records to limit usage of same SID for user and group
|
|
* - fixed decoding SIDs on 64-bit systems
|
|
* - fixed a pointer to dynamic data in mapping tables
|
|
* - fixed default mapping on Windows
|
|
* - fixed bug for renaming UserMapping on Windows
|
|
*
|
|
* May 2009 Version 1.1.2
|
|
* - avoided selecting DOS names on Linux
|
|
*
|
|
* Nov 2009 Version 1.1.3
|
|
* - silenced compiler warnings for unused parameters
|
|
*
|
|
* Jan 2010 Version 1.1.4
|
|
* - fixed compilation problems for Mac OSX (Erik Larsson)
|
|
*
|
|
* Apr 2014 Version 1.1.5
|
|
* - displayed the parent directory of selected files
|
|
*
|
|
* May 2014 Version 1.1.6
|
|
* - fixed a wrong function header
|
|
*
|
|
* Mar 2016 Version 1.2.0
|
|
* - reorganized to rely on libntfs-3g even on Windows
|
|
*/
|
|
|
|
/*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program (in the main directory of the NTFS-3G
|
|
* distribution in the file COPYING); if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
/*
|
|
* General parameters which may have to be adapted to needs
|
|
*/
|
|
|
|
#define USERMAPVERSION "1.2.0"
|
|
#define MAPDIR ".NTFS-3G"
|
|
#define MAPFILE "UserMapping"
|
|
#define MAXATTRSZ 2048
|
|
#define MAXSIDSZ 80
|
|
#define MAXNAMESZ 256
|
|
#define OWNERS1 "Documents and Settings"
|
|
#define OWNERS2 "Users"
|
|
|
|
#include "config.h"
|
|
|
|
#ifdef HAVE_STDIO_H
|
|
#include <stdio.h>
|
|
#endif
|
|
#ifdef HAVE_STDLIB_H
|
|
#include <stdlib.h>
|
|
#endif
|
|
#ifdef HAVE_STRING_H
|
|
#include <string.h>
|
|
#endif
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
#ifdef HAVE_TIME_H
|
|
#include <time.h>
|
|
#endif
|
|
#ifdef HAVE_GETOPT_H
|
|
#include <getopt.h>
|
|
#endif
|
|
#ifdef HAVE_ERRNO_H
|
|
#include <errno.h>
|
|
#endif
|
|
#ifdef HAVE_FCNTL_H
|
|
#include <fcntl.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_TYPES_H
|
|
#include <sys/types.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_STAT_H
|
|
#include <sys/stat.h>
|
|
#endif
|
|
|
|
#include "types.h"
|
|
#include "endians.h"
|
|
#include "support.h"
|
|
#include "layout.h"
|
|
#include "param.h"
|
|
#include "ntfstime.h"
|
|
#include "device_io.h"
|
|
#include "device.h"
|
|
#include "logging.h"
|
|
#include "runlist.h"
|
|
#include "mft.h"
|
|
#include "inode.h"
|
|
#include "attrib.h"
|
|
#include "bitmap.h"
|
|
#include "index.h"
|
|
#include "volume.h"
|
|
#include "unistr.h"
|
|
#include "mst.h"
|
|
#include "security.h"
|
|
#include "utils.h"
|
|
#include "misc.h"
|
|
|
|
#ifdef HAVE_WINDOWS_H
|
|
/*
|
|
* Including <windows.h> leads to numerous conflicts with layout.h
|
|
* so define a few needed Windows calls unrelated to ntfs-3g
|
|
*/
|
|
BOOL WINAPI LookupAccountNameA(const char*, const char*, void*,
|
|
u32*, char*, u32*, s32*);
|
|
BOOL WINAPI GetUserNameA(char*, u32*);
|
|
#endif
|
|
|
|
#ifdef HAVE_WINDOWS_H
|
|
#define DIRSEP "\\"
|
|
#else
|
|
#define DIRSEP "/"
|
|
#endif
|
|
|
|
#ifdef HAVE_WINDOWS_H
|
|
#define BANNER "Generated by ntfsusermap for Windows, v " USERMAPVERSION
|
|
#else
|
|
#define BANNER "Generated by ntfsusermap for Linux, v " USERMAPVERSION
|
|
#endif
|
|
|
|
typedef enum { DENIED, AGREED } boolean;
|
|
|
|
enum STATES { STATE_USERS, STATE_HOMES, STATE_BASE } ;
|
|
|
|
struct CALLBACK {
|
|
const char *accname;
|
|
const char *dir;
|
|
int levels;
|
|
enum STATES docset;
|
|
} ;
|
|
|
|
typedef int (*dircallback)(struct CALLBACK *context, char *ntfsname,
|
|
int length, int type, long long pos, unsigned long long mft_ref,
|
|
unsigned int dt_type);
|
|
|
|
|
|
struct USERMAPPING {
|
|
struct USERMAPPING *next;
|
|
const char *uidstr;
|
|
const char *gidstr;
|
|
const char *sidstr;
|
|
const unsigned char *sid;
|
|
const char *login;
|
|
boolean defined;
|
|
};
|
|
|
|
struct USERMAPPING *firstmapping;
|
|
struct USERMAPPING *lastmapping;
|
|
|
|
#ifdef HAVE_WINDOWS_H
|
|
char *currentwinname;
|
|
char *currentdomain;
|
|
unsigned char *currentsid;
|
|
#endif
|
|
|
|
void *ntfs_handle;
|
|
void *ntfs_context = (void*)NULL;
|
|
|
|
/*
|
|
* Open and close a volume in read-only mode
|
|
* assuming a single volume needs to be opened at any time
|
|
*/
|
|
|
|
static boolean open_volume(const char *volume)
|
|
{
|
|
boolean ok;
|
|
|
|
ok = DENIED;
|
|
if (!ntfs_context) {
|
|
ntfs_context = ntfs_initialize_file_security(volume,
|
|
NTFS_MNT_RDONLY);
|
|
if (ntfs_context) {
|
|
fprintf(stderr,"\"%s\" opened\n",volume);
|
|
ok = AGREED;
|
|
} else {
|
|
fprintf(stderr,"Could not open \"%s\"\n",volume);
|
|
#ifdef HAVE_WINDOWS_H
|
|
if (errno == EACCES)
|
|
fprintf(stderr,"Make sure you have"
|
|
" Administrator rights\n");
|
|
#else
|
|
fprintf(stderr,"Make sure \"%s\" is not mounted\n",
|
|
volume);
|
|
#endif
|
|
}
|
|
} else
|
|
fprintf(stderr,"A volume is already open\n");
|
|
return (ok);
|
|
}
|
|
|
|
static boolean close_volume(const char *volume)
|
|
{
|
|
boolean r;
|
|
|
|
r = ntfs_leave_file_security(ntfs_context);
|
|
if (r)
|
|
fprintf(stderr,"\"%s\" closed\n",volume);
|
|
else
|
|
fprintf(stderr,"Could not close \"%s\"\n",volume);
|
|
ntfs_context = (void*)NULL;
|
|
return (r);
|
|
}
|
|
|
|
/*
|
|
* A poor man's conversion of Unicode to UTF8
|
|
* We are assuming outputs to terminal expect UTF8
|
|
*/
|
|
|
|
static void to_utf8(char *dst, const char *src, unsigned int cnt)
|
|
{
|
|
unsigned int ch;
|
|
unsigned int i;
|
|
|
|
for (i=0; i<cnt; i++) {
|
|
ch = *src++ & 255;
|
|
ch += (*src++ & 255) << 8;
|
|
if (ch < 0x80)
|
|
*dst++ = ch;
|
|
else
|
|
if (ch < 0x1000) {
|
|
*dst++ = 0xc0 + (ch >> 6);
|
|
*dst++ = 0x80 + (ch & 63);
|
|
} else {
|
|
*dst++ = 0xe0 + (ch >> 12);
|
|
*dst++ = 0x80 + ((ch >> 6) & 63);
|
|
*dst++ = 0x80 + (ch & 63);
|
|
}
|
|
}
|
|
*dst = 0;
|
|
}
|
|
|
|
static int utf8_size(const char *src, unsigned int cnt)
|
|
{
|
|
unsigned int ch;
|
|
unsigned int i;
|
|
int size;
|
|
|
|
size = 0;
|
|
for (i=0; i<cnt; i++) {
|
|
ch = *src++ & 255;
|
|
ch += (*src++ & 255) << 8;
|
|
if (ch < 0x80)
|
|
size++;
|
|
else
|
|
if (ch < 0x1000)
|
|
size += 2;
|
|
else
|
|
size += 3;
|
|
}
|
|
return (size);
|
|
}
|
|
|
|
static void welcome(void)
|
|
{
|
|
printf("\nThis tool will help you to build a mapping of"
|
|
" Windows users\n");
|
|
printf("to Linux users.\n");
|
|
printf("Be prepared to give Linux user id (uid) and group id (gid)\n");
|
|
printf("for owners of files which will be selected.\n");
|
|
}
|
|
|
|
static unsigned int get2l(const unsigned char *attr, int p)
|
|
{
|
|
int i;
|
|
unsigned int v;
|
|
|
|
v = 0;
|
|
for (i = 0; i < 2; i++)
|
|
v += (attr[p + i] & 255) << (8 * i);
|
|
return (v);
|
|
}
|
|
|
|
static unsigned long get4l(const unsigned char *attr, int p)
|
|
{
|
|
int i;
|
|
unsigned long v;
|
|
|
|
v = 0;
|
|
for (i = 0; i < 4; i++)
|
|
v += (attr[p + i] & 255L) << (8 * i);
|
|
return (v);
|
|
}
|
|
|
|
static unsigned long long get6h(const unsigned char *attr, int p)
|
|
{
|
|
int i;
|
|
unsigned long long v;
|
|
|
|
v = 0;
|
|
for (i = 0; i < 6; i++)
|
|
v = (v << 8) + (attr[p + i] & 255L);
|
|
return (v);
|
|
}
|
|
|
|
static char *decodesid(const unsigned char *sid)
|
|
{
|
|
char *str;
|
|
int i;
|
|
unsigned long long auth;
|
|
unsigned long subauth;
|
|
|
|
str = (char *)malloc(MAXSIDSZ);
|
|
if (str) {
|
|
strcpy(str, "S");
|
|
/* revision */
|
|
sprintf(&str[strlen(str)], "-%d", sid[0]);
|
|
/* main authority */
|
|
auth = get6h(sid, 2);
|
|
#ifdef HAVE_WINDOWS_H
|
|
sprintf(&str[strlen(str)], "-%I64u", auth);
|
|
#else
|
|
sprintf(&str[strlen(str)], "-%llu", auth);
|
|
#endif
|
|
for (i = 0; (i < 8) && (i < sid[1]); i++) {
|
|
/* sub-authority */
|
|
subauth = get4l(sid, 8 + 4 * i);
|
|
sprintf(&str[strlen(str)], "-%lu", subauth);
|
|
}
|
|
}
|
|
return (str);
|
|
}
|
|
|
|
/*
|
|
* Test whether a generic group (S-1-5-21- ... -513)
|
|
*/
|
|
|
|
static boolean isgenericgroup(const char *sid)
|
|
{
|
|
boolean yes;
|
|
|
|
yes = !strncmp(sid,"S-1-5-21-",9)
|
|
&& !strcmp(strrchr(sid,'-'),"-513");
|
|
return (yes);
|
|
}
|
|
|
|
static unsigned char *makegroupsid(const unsigned char *sid)
|
|
{
|
|
static unsigned char groupsid[MAXSIDSZ];
|
|
int size;
|
|
|
|
size = 8 + 4*sid[1];
|
|
memcpy(groupsid, sid, size);
|
|
/* replace last level by 513 */
|
|
groupsid[size - 4] = 1;
|
|
groupsid[size - 3] = 2;
|
|
groupsid[size - 2] = 0;
|
|
groupsid[size - 1] = 0;
|
|
return (groupsid);
|
|
}
|
|
|
|
static void askmapping(const char *accname, const char *filename,
|
|
const char *dir, const unsigned char *sid, int type,
|
|
struct USERMAPPING *mapping, char *sidstr)
|
|
{
|
|
char buf[81];
|
|
char *idstr;
|
|
char *login;
|
|
int sidsz;
|
|
boolean reject;
|
|
char *p;
|
|
|
|
do {
|
|
reject = DENIED;
|
|
printf("\n");
|
|
if (accname)
|
|
printf("Under Windows login \"%s\"\n", accname);
|
|
if (dir) {
|
|
#ifdef HAVE_WINDOWS_H
|
|
char *wdir;
|
|
|
|
wdir = strdup(dir);
|
|
if (wdir) {
|
|
for (p=wdir; *p; p++)
|
|
if (*p == '/')
|
|
*p = '\\';
|
|
printf(" in directory \"%s\"\n",wdir);
|
|
free(wdir);
|
|
}
|
|
#else
|
|
printf(" in directory \"%s\"\n",dir);
|
|
#endif
|
|
}
|
|
printf(" file \"%s\" has no mapped %s\n",
|
|
filename,(type ? "group" : "owner"));
|
|
printf("By which Linux login should this file be owned ?\n");
|
|
printf("Enter %s of login, or just press \"enter\" if"
|
|
" this file\n", (type ? "gid" : "uid"));
|
|
printf("does not belong to a user, or you do not know"
|
|
" to whom\n");
|
|
printf("\n");
|
|
if (type)
|
|
printf("Group : ");
|
|
else
|
|
printf("User : ");
|
|
p = fgets(buf, 80, stdin);
|
|
if (p && p[0] && (p[strlen(p) - 1] == '\n'))
|
|
p[strlen(p) - 1] = '\0';
|
|
|
|
if (p && p[0]
|
|
&& ((p[0] == '0') || !strcmp(p, "root"))) {
|
|
printf("Please do not map users to root\n");
|
|
printf("Administrators will be mapped automatically\n");
|
|
reject = AGREED;
|
|
}
|
|
if (reject)
|
|
printf("Please retry\n");
|
|
} while (reject);
|
|
if (!mapping) {
|
|
mapping =
|
|
(struct USERMAPPING *)
|
|
malloc(sizeof(struct USERMAPPING));
|
|
mapping->next = (struct USERMAPPING *)NULL;
|
|
mapping->defined = DENIED;
|
|
if (lastmapping)
|
|
lastmapping->next = mapping;
|
|
else
|
|
firstmapping = mapping;
|
|
lastmapping = mapping;
|
|
}
|
|
if (mapping) {
|
|
if (p && p[0]) {
|
|
idstr = (char *)malloc(strlen(p) + 1);
|
|
if (idstr) {
|
|
strcpy(idstr, p);
|
|
if (type) {
|
|
mapping->uidstr = "";
|
|
mapping->gidstr = idstr;
|
|
} else {
|
|
mapping->uidstr = idstr;
|
|
mapping->gidstr = idstr;
|
|
}
|
|
mapping->defined = AGREED;
|
|
}
|
|
}
|
|
mapping->sidstr = sidstr;
|
|
if (accname) {
|
|
login = (char*)malloc(strlen(accname) + 1);
|
|
if (login)
|
|
strcpy(login,accname);
|
|
mapping->login = login;
|
|
} else
|
|
mapping->login = (char*)NULL;
|
|
sidsz = 8 + sid[1]*4;
|
|
p = (char*)malloc(sidsz);
|
|
if (p) {
|
|
memcpy(p, sid, sidsz);
|
|
}
|
|
mapping->sid = (unsigned char*)p;
|
|
}
|
|
}
|
|
|
|
static void domapping(const char *accname, const char *filename,
|
|
const char *dir, const unsigned char *sid, int type)
|
|
{
|
|
char *sidstr;
|
|
struct USERMAPPING *mapping;
|
|
|
|
if ((get6h(sid, 2) == 5) && (get4l(sid, 8) == 21)) {
|
|
sidstr = decodesid(sid);
|
|
mapping = firstmapping;
|
|
while (mapping && strcmp(mapping->sidstr, sidstr))
|
|
mapping = mapping->next;
|
|
if (mapping
|
|
&& (mapping->defined
|
|
|| !accname
|
|
|| !strcmp(mapping->login, accname)))
|
|
free(sidstr); /* decision already known */
|
|
else {
|
|
askmapping(accname, filename, dir, sid, type,
|
|
mapping, sidstr);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void listaclusers(const char *accname, const unsigned char *attr,
|
|
int off)
|
|
{
|
|
int i;
|
|
int cnt;
|
|
int x;
|
|
|
|
cnt = get2l(attr, off + 4);
|
|
x = 8;
|
|
for (i = 0; i < cnt; i++) {
|
|
domapping(accname, (char *)NULL, (char*)NULL,
|
|
&attr[off + x + 8], 2);
|
|
x += get2l(attr, off + x + 2);
|
|
}
|
|
}
|
|
|
|
static void account(const char *accname, const char *dir,
|
|
const char *name, int type)
|
|
{
|
|
unsigned char attr[MAXATTRSZ];
|
|
u32 attrsz;
|
|
char *fullname;
|
|
|
|
fullname = (char *)malloc(strlen(dir) + strlen(name) + 2);
|
|
if (fullname) {
|
|
strcpy(fullname, dir);
|
|
strcat(fullname, "/");
|
|
strcat(fullname, name);
|
|
if (ntfs_get_file_security(ntfs_context,
|
|
fullname, OWNER_SECURITY_INFORMATION,
|
|
(char*)attr, MAXATTRSZ, &attrsz)) {
|
|
domapping(accname, name, dir, &attr[20], 0);
|
|
attrsz = 0;
|
|
if (ntfs_get_file_security(ntfs_context,
|
|
fullname, GROUP_SECURITY_INFORMATION,
|
|
(char*)attr, MAXATTRSZ, &attrsz))
|
|
domapping(accname, name, dir, &attr[20], 1);
|
|
else
|
|
printf(" No group SID\n");
|
|
attrsz = 0;
|
|
if (ntfs_get_file_security(ntfs_context,
|
|
fullname, DACL_SECURITY_INFORMATION,
|
|
(char*)attr, MAXATTRSZ, &attrsz)) {
|
|
if (type == 0)
|
|
listaclusers(accname, attr, 20);
|
|
} else
|
|
printf(" No discretionary access control"
|
|
" list for %s !\n", dir);
|
|
}
|
|
free(fullname);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* recursive search of file owners and groups in a directory
|
|
*/
|
|
|
|
static boolean recurse(const char *accname, const char *dir,
|
|
int levels, enum STATES docset);
|
|
|
|
static int callback(void *ctx, const ntfschar *ntfsname,
|
|
const int length, const int type,
|
|
const s64 pos __attribute__((unused)),
|
|
const MFT_REF mft_ref __attribute__((unused)),
|
|
const unsigned int dt_type __attribute__((unused)))
|
|
{
|
|
struct CALLBACK *context;
|
|
char *fullname;
|
|
char *accname;
|
|
char *name;
|
|
|
|
context = (struct CALLBACK*)ctx;
|
|
fullname = (char *)malloc(strlen(context->dir)
|
|
+ utf8_size((const char*)ntfsname, length) + 2);
|
|
if (fullname) {
|
|
/* No "\\" when interfacing libntfs-3g */
|
|
if (strcmp(context->dir,"/")) {
|
|
strcpy(fullname, context->dir);
|
|
strcat(fullname, "/");
|
|
} else
|
|
strcpy(fullname,"/");
|
|
/* Unicode to ascii conversion by a lazy man */
|
|
name = &fullname[strlen(fullname)];
|
|
to_utf8(name, (const char*)ntfsname, length);
|
|
/* ignore special files and DOS names */
|
|
if ((type != 2)
|
|
&& strcmp(name,".")
|
|
&& strcmp(name,"..")
|
|
&& (name[0] != '$')) {
|
|
switch (context->docset) {
|
|
case STATE_USERS :
|
|
/*
|
|
* only "Documents and Settings"
|
|
* or "Users"
|
|
*/
|
|
if (!strcmp(name,OWNERS1)
|
|
|| !strcmp(name,OWNERS2)) {
|
|
recurse((char*)NULL, fullname, 2,
|
|
STATE_HOMES);
|
|
}
|
|
break;
|
|
/*
|
|
* within "Documents and Settings"
|
|
* or "Users"
|
|
*/
|
|
case STATE_HOMES :
|
|
accname = (char*)malloc(strlen(name) + 1);
|
|
if (accname) {
|
|
strcpy(accname, name);
|
|
if (context->levels > 0)
|
|
recurse(name, fullname,
|
|
context->levels - 1,
|
|
STATE_BASE);
|
|
}
|
|
break;
|
|
/*
|
|
* not related to "Documents and
|
|
* Settings" or "Users"
|
|
*/
|
|
case STATE_BASE :
|
|
account(context->accname, context->dir,
|
|
name, 1);
|
|
if (context->levels > 0)
|
|
recurse(context->accname, fullname,
|
|
context->levels - 1,
|
|
STATE_BASE);
|
|
break;
|
|
}
|
|
}
|
|
free(fullname);
|
|
}
|
|
/* check expected return value */
|
|
return (0);
|
|
}
|
|
|
|
static boolean recurse(const char *accname, const char *dir,
|
|
int levels, enum STATES docset)
|
|
{
|
|
struct CALLBACK context;
|
|
boolean err;
|
|
|
|
err = DENIED;
|
|
context.dir = dir;
|
|
context.accname = accname;
|
|
context.levels = levels;
|
|
context.docset = docset;
|
|
ntfs_read_directory(ntfs_context,dir,callback,&context);
|
|
return (!err);
|
|
}
|
|
|
|
/*
|
|
* Search directory "Documents and Settings" for user accounts
|
|
*/
|
|
|
|
static boolean getusers(const char *dir, int levels)
|
|
{
|
|
boolean err;
|
|
struct CALLBACK context;
|
|
|
|
printf("* Search for \"" OWNERS1 "\" and \"" OWNERS2 "\"\n");
|
|
err = DENIED;
|
|
context.dir = dir;
|
|
context.accname = (const char*)NULL;
|
|
context.levels = levels;
|
|
context.docset = STATE_USERS;
|
|
ntfs_read_directory(ntfs_context,dir,callback,&context);
|
|
printf("* Search for other directories %s\n",dir);
|
|
context.docset = STATE_BASE;
|
|
ntfs_read_directory(ntfs_context,dir,callback,&context);
|
|
return (!err);
|
|
}
|
|
|
|
#ifdef HAVE_WINDOWS_H
|
|
/*
|
|
* Get the current login name (Win32 only)
|
|
*/
|
|
|
|
static void loginname(boolean silent)
|
|
{
|
|
char *winname;
|
|
char *domain;
|
|
unsigned char *sid;
|
|
u32 namesz;
|
|
u32 sidsz;
|
|
u32 domainsz;
|
|
s32 nametype;
|
|
boolean ok;
|
|
int r;
|
|
|
|
ok = FALSE;
|
|
winname = (char*)malloc(MAXNAMESZ);
|
|
domain = (char*)malloc(MAXNAMESZ);
|
|
sid = (char*)malloc(MAXSIDSZ);
|
|
|
|
namesz = MAXNAMESZ;
|
|
domainsz = MAXNAMESZ;
|
|
sidsz = MAXSIDSZ;
|
|
if (winname
|
|
&& domain
|
|
&& sid
|
|
&& GetUserNameA(winname,&namesz)) {
|
|
winname[namesz] = '\0';
|
|
if (!silent)
|
|
printf("Your current user name is %s\n",winname);
|
|
nametype = 1;
|
|
r = LookupAccountNameA((char*)NULL,winname,sid,&sidsz,
|
|
domain,&domainsz,&nametype);
|
|
if (r) {
|
|
domain[domainsz] = '\0';
|
|
if (!silent)
|
|
printf("Your account domain is %s\n",domain);
|
|
ok = AGREED;
|
|
}
|
|
}
|
|
if (ok) {
|
|
currentwinname = winname;
|
|
currentdomain = domain;
|
|
currentsid = sid;
|
|
} else {
|
|
currentwinname = (char*)NULL;
|
|
currentdomain = (char*)NULL;
|
|
currentsid = (unsigned char*)NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Minimal output on stdout
|
|
*/
|
|
|
|
static boolean minimal(unsigned char *sid)
|
|
{
|
|
const unsigned char *groupsid;
|
|
boolean ok;
|
|
|
|
ok = DENIED;
|
|
if (sid) {
|
|
groupsid = makegroupsid(sid);
|
|
printf("# %s\n",BANNER);
|
|
printf("# For Windows account \"%s\" in domain \"%s\"\n",
|
|
currentwinname, currentdomain);
|
|
printf("# Replace \"user\" and \"group\" hereafter by"
|
|
" matching Linux login\n");
|
|
printf("user::%s\n",decodesid(sid));
|
|
printf(":group:%s\n",decodesid(groupsid));
|
|
ok = AGREED;
|
|
}
|
|
return (ok);
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
* Create a user mapping file
|
|
*
|
|
* From now on, partitions which were opened through ntfs-3g
|
|
* are closed, and we use the system drivers to create the file.
|
|
* On Windows, we can write on a partition which was analyzed.
|
|
*/
|
|
|
|
static boolean outputmap(const char *volume, const char *dir)
|
|
{
|
|
char buf[256];
|
|
int fn;
|
|
char *fullname;
|
|
char *backup;
|
|
struct USERMAPPING *mapping;
|
|
boolean done;
|
|
boolean err;
|
|
boolean undecided;
|
|
struct stat st;
|
|
int s;
|
|
|
|
done = DENIED;
|
|
fullname = (char *)malloc(strlen(MAPFILE) + 1
|
|
+ strlen(volume) + 1
|
|
+ (dir ? strlen(dir) + 1 : 0));
|
|
if (fullname) {
|
|
#ifdef HAVE_WINDOWS_H
|
|
strcpy(fullname, volume);
|
|
if (dir && dir[0]) {
|
|
strcat(fullname, DIRSEP);
|
|
strcat(fullname,dir);
|
|
}
|
|
|
|
/* build directory, if not present */
|
|
if (stat(fullname,&st) && (errno == ENOENT)) {
|
|
printf("* Creating directory %s\n", fullname);
|
|
mkdir(fullname);
|
|
}
|
|
|
|
strcat(fullname, DIRSEP);
|
|
strcat(fullname, MAPFILE);
|
|
printf("\n");
|
|
#else
|
|
strcpy(fullname, MAPFILE);
|
|
printf("\n");
|
|
#endif
|
|
|
|
s = stat(fullname,&st);
|
|
if (!s) {
|
|
backup = (char*)malloc(strlen(fullname + 5));
|
|
strcpy(backup,fullname);
|
|
strcat(backup,".bak");
|
|
#ifdef HAVE_WINDOWS_H
|
|
unlink(backup);
|
|
#endif
|
|
if (rename(fullname,backup))
|
|
printf("* Old mapping file moved to %s\n",
|
|
backup);
|
|
}
|
|
|
|
printf("* Creating file %s\n", fullname);
|
|
err = DENIED;
|
|
#ifdef HAVE_WINDOWS_H
|
|
fn = open(fullname,O_CREAT + O_TRUNC + O_WRONLY + O_BINARY,
|
|
S_IREAD + S_IWRITE);
|
|
#else
|
|
fn = open(fullname,O_CREAT + O_TRUNC + O_WRONLY,
|
|
S_IREAD + S_IWRITE);
|
|
#endif
|
|
if (fn > 0) {
|
|
sprintf(buf,"# %s\n",BANNER);
|
|
if (!write(fn,buf,strlen(buf)))
|
|
err = AGREED;
|
|
printf("%s",buf);
|
|
undecided = DENIED;
|
|
/* records for owner only or group only */
|
|
for (mapping = firstmapping; mapping && !err;
|
|
mapping = mapping->next)
|
|
if (mapping->defined
|
|
&& (!mapping->uidstr[0]
|
|
|| !mapping->gidstr[0])) {
|
|
sprintf(buf,"%s:%s:%s\n",
|
|
mapping->uidstr,
|
|
mapping->gidstr,
|
|
mapping->sidstr);
|
|
if (!write(fn,buf,strlen(buf)))
|
|
err = AGREED;
|
|
printf("%s",buf);
|
|
} else
|
|
undecided = AGREED;
|
|
/* records for both owner and group */
|
|
for (mapping = firstmapping; mapping && !err;
|
|
mapping = mapping->next)
|
|
if (mapping->defined
|
|
&& mapping->uidstr[0]
|
|
&& mapping->gidstr[0]) {
|
|
sprintf(buf,"%s:%s:%s\n",
|
|
mapping->uidstr,
|
|
mapping->gidstr,
|
|
mapping->sidstr);
|
|
if (!write(fn,buf,strlen(buf)))
|
|
err = AGREED;
|
|
printf("%s",buf);
|
|
} else
|
|
undecided = AGREED;
|
|
done = !err;
|
|
close(fn);
|
|
if (undecided) {
|
|
printf("Undecided :\n");
|
|
for (mapping = firstmapping; mapping;
|
|
mapping = mapping->next)
|
|
if (!mapping->defined) {
|
|
printf(" %s\n",
|
|
mapping->sidstr);
|
|
}
|
|
}
|
|
#ifndef HAVE_WINDOWS_H
|
|
printf("\n* You will have to move the file \""
|
|
MAPFILE "\"\n");
|
|
printf(" to directory \"" MAPDIR "\" after"
|
|
" mounting\n");
|
|
#endif
|
|
}
|
|
}
|
|
if (!done)
|
|
fprintf(stderr, "* Could not create mapping file \"%s\"\n",
|
|
fullname);
|
|
return (done);
|
|
}
|
|
|
|
static boolean sanitize(void)
|
|
{
|
|
char buf[81];
|
|
boolean ok;
|
|
int ownercnt;
|
|
int groupcnt;
|
|
struct USERMAPPING *mapping;
|
|
struct USERMAPPING *firstowner;
|
|
struct USERMAPPING *genericgroup;
|
|
struct USERMAPPING *group;
|
|
char *sidstr;
|
|
|
|
/* count owners and groups */
|
|
/* and find first user, and a generic group */
|
|
ownercnt = 0;
|
|
groupcnt = 0;
|
|
firstowner = (struct USERMAPPING*)NULL;
|
|
genericgroup = (struct USERMAPPING*)NULL;
|
|
for (mapping=firstmapping; mapping; mapping=mapping->next) {
|
|
if (mapping->defined && mapping->uidstr[0]) {
|
|
if (!ownercnt)
|
|
firstowner = mapping;
|
|
ownercnt++;
|
|
}
|
|
if (mapping->defined && mapping->gidstr[0]
|
|
&& !mapping->uidstr[0]) {
|
|
groupcnt++;
|
|
}
|
|
if (!mapping->defined && isgenericgroup(mapping->sidstr)) {
|
|
genericgroup = mapping;
|
|
}
|
|
}
|
|
#ifdef HAVE_WINDOWS_H
|
|
/* no user defined, on Windows, suggest a mapping */
|
|
/* based on account currently used */
|
|
if (!ownercnt && currentwinname && currentsid) {
|
|
char *owner;
|
|
char *p;
|
|
|
|
printf("\nYou have defined no file owner,\n");
|
|
printf(" please enter the Linux login which should"
|
|
" be mapped\n");
|
|
printf(" to account you are currently using\n");
|
|
printf(" Linux user ? ");
|
|
p = fgets(buf, 80, stdin);
|
|
if (p && p[0] && (p[strlen(p) - 1] == '\n'))
|
|
p[strlen(p) - 1] = '\0';
|
|
if (p && p[0]) {
|
|
firstowner = (struct USERMAPPING*)malloc(
|
|
sizeof(struct USERMAPPING));
|
|
owner = (char*)malloc(strlen(p) + 1);
|
|
if (firstowner && owner) {
|
|
strcpy(owner, p);
|
|
firstowner->next = firstmapping;
|
|
firstowner->uidstr = owner;
|
|
firstowner->gidstr = "";
|
|
firstowner->sidstr = decodesid(currentsid);
|
|
firstowner->sid = currentsid;
|
|
firstmapping = firstowner;
|
|
ownercnt++;
|
|
/* prefer a generic group with the same
|
|
* authorities */
|
|
for (mapping=firstmapping; mapping;
|
|
mapping=mapping->next)
|
|
if (!mapping->defined
|
|
&& isgenericgroup(mapping->sidstr)
|
|
&& !memcmp(firstowner->sidstr,
|
|
mapping->sidstr,
|
|
strlen(mapping
|
|
->sidstr)-3))
|
|
genericgroup = mapping;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
if (ownercnt) {
|
|
/*
|
|
* No group was selected, but there were a generic
|
|
* group, insist in using it, associated to the
|
|
* first user
|
|
*/
|
|
if (!groupcnt) {
|
|
printf("\nYou have defined no group,"
|
|
" this can cause problems\n");
|
|
printf("Do you accept defining a standard group ?\n");
|
|
if (!fgets(buf,80,stdin)
|
|
|| ((buf[0] != 'n')
|
|
&& (buf[0] != 'N'))) {
|
|
if (genericgroup) {
|
|
genericgroup->uidstr = "";
|
|
genericgroup->gidstr =
|
|
firstowner->uidstr;
|
|
genericgroup->defined = AGREED;
|
|
} else {
|
|
group = (struct USERMAPPING*)
|
|
malloc(sizeof(
|
|
struct USERMAPPING));
|
|
sidstr = decodesid(
|
|
makegroupsid(firstowner->sid));
|
|
if (group && sidstr) {
|
|
group->uidstr = "";
|
|
group->gidstr = firstowner->
|
|
uidstr;
|
|
group->sidstr = sidstr;
|
|
group->defined = AGREED;
|
|
group->next = firstmapping;
|
|
firstmapping = group;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ok = AGREED;
|
|
} else {
|
|
printf("\nYou have defined no user, no mapping can be built\n");
|
|
ok = DENIED;
|
|
}
|
|
|
|
return (ok);
|
|
}
|
|
|
|
static boolean checkoptions(int argc, char *argv[] __attribute__((unused)),
|
|
boolean silent __attribute__((unused)))
|
|
{
|
|
boolean err;
|
|
#ifdef HAVE_WINDOWS_H
|
|
int xarg;
|
|
const char *pvol;
|
|
|
|
if (silent) {
|
|
err = (argc != 1);
|
|
} else {
|
|
err = (argc < 2);
|
|
for (xarg=1; (xarg<argc) && !err; xarg++) {
|
|
pvol = argv[xarg];
|
|
if (pvol[0] && (pvol[1] == ':') && !pvol[2]) {
|
|
err = !(((pvol[0] >= 'A') && (pvol[0] <= 'Z'))
|
|
|| ((pvol[0] >= 'a')
|
|
&& (pvol[0] <= 'z')));
|
|
}
|
|
}
|
|
}
|
|
if (err) {
|
|
fprintf(stderr, "Usage : ntfsusermap [vol1: [vol2: ...]]\n");
|
|
fprintf(stderr, " \"voln\" are the letters of the partition"
|
|
" to share with Linux\n");
|
|
fprintf(stderr, " eg C:\n");
|
|
fprintf(stderr, " the Windows system partition should be"
|
|
" named first\n");
|
|
if (silent) {
|
|
fprintf(stderr, "When outputting to file, a minimal"
|
|
" user mapping proposal\n");
|
|
fprintf(stderr, "is written to the file, and no"
|
|
" partition should be mentioned\n");
|
|
}
|
|
}
|
|
#else
|
|
err = (argc < 2);
|
|
if (err) {
|
|
fprintf(stderr, "Usage : ntfsusermap dev1 [dev2 ...]\n");
|
|
fprintf(stderr, " \"dev.\" are the devices to share"
|
|
" with Windows\n");
|
|
fprintf(stderr, " eg /dev/sdb1\n");
|
|
fprintf(stderr, " the devices should not be mounted, and\n");
|
|
fprintf(stderr, " the Windows system partition should"
|
|
" be named first\n");
|
|
} else
|
|
if (getuid()) {
|
|
fprintf(stderr, "\nSorry, only root can start"
|
|
" ntfsusermap\n");
|
|
err = AGREED;
|
|
}
|
|
#endif
|
|
return (!err);
|
|
}
|
|
|
|
static boolean process(int argc, char *argv[])
|
|
{
|
|
boolean ok;
|
|
int xarg;
|
|
int targ;
|
|
|
|
firstmapping = (struct USERMAPPING *)NULL;
|
|
lastmapping = (struct USERMAPPING *)NULL;
|
|
ok = AGREED;
|
|
for (xarg=1; (xarg<argc) && ok; xarg++)
|
|
if (open_volume(argv[xarg])) {
|
|
printf("\n* Scanning \"%s\" (two levels)\n",argv[xarg]);
|
|
ok = getusers("/",2);
|
|
close_volume(argv[xarg]);
|
|
} else
|
|
ok = DENIED;
|
|
if (ok && sanitize()) {
|
|
targ = (argc > 2 ? 2 : 1);
|
|
if (!outputmap(argv[targ],MAPDIR)) {
|
|
printf("Trying to write file on root directory\n");
|
|
if (outputmap(argv[targ],(const char*)NULL)) {
|
|
printf("\nNote : you will have to move the"
|
|
" file to directory \"%s\" on Linux\n",
|
|
MAPDIR);
|
|
} else
|
|
ok = DENIED;
|
|
} else
|
|
ok = DENIED;
|
|
} else
|
|
ok = DENIED;
|
|
return (ok);
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
boolean ok;
|
|
boolean silent;
|
|
|
|
silent = !isatty(1);
|
|
if (!silent)
|
|
welcome();
|
|
if (checkoptions(argc, argv, silent)) {
|
|
#ifdef HAVE_WINDOWS_H
|
|
loginname(silent);
|
|
if (silent)
|
|
ok = minimal(currentsid);
|
|
else
|
|
ok = process(argc,argv);
|
|
#else
|
|
ok = process(argc,argv);
|
|
#endif
|
|
} else
|
|
ok = DENIED;
|
|
if (!ok)
|
|
exit(1);
|
|
return (0);
|
|
}
|