diff --git a/.downloads/ncpfs-0.6.tgz b/.downloads/ncpfs-0.6.tgz new file mode 100644 index 0000000..dc42af0 Binary files /dev/null and b/.downloads/ncpfs-0.6.tgz differ diff --git a/BUGS b/BUGS index cea62ba..ec20fff 100644 --- a/BUGS +++ b/BUGS @@ -3,15 +3,17 @@ them to be bugs. But there are really problems that might be fixed in the future. -Invalid directory timestamps: -I did not yet find out how to get valid timestamps for directories -from a NetWare server. So I simply return 0, which means 01.01.70. If -anybody knows how to get these values, please mail -lendecke@namu01.gwdg.de. - 'df' returns 0: Free disk space is distributed among the volumes in NetWare. df is only able to report one number per mounted filesystem. As connections are quite expensive for NetWare (with lwared that might change ...), I rejected the alternative to mount only a single volume for a unix mount point. So I simply return 0. + + +In your kernel log, there will appear messages like + +Nov 25 16:09:08 lx01 kernel: alloc_skb called nonatomically from interrupt 0000002e + +These are a bit annoying, but completely harmless. Maybe this will be +fixed in the future. diff --git a/Makefile b/Makefile index 6f31368..1e34194 100644 --- a/Makefile +++ b/Makefile @@ -2,76 +2,45 @@ # Makefile for the linux ncp-filesystem routines. # -INCLUDES = -I/usr/src/linux/include +KERNEL = 1.3 -CFLAGS = -Wall -Wstrict-prototypes -O2 -DMODULE -fomit-frame-pointer \ - $(INCLUDES) \ -# -DDEBUG_NCP=2 -DDEBUG_NCP_MALLOC -# -DDEBUG_NCP_MALLOC +INCLUDES = -I/usr/src/linux/include -Ikernel +BINDIR = ./bin -CC = gcc -D__KERNEL__ -I. -AS = as -ARCH = i386 +CFLAGS = -Wall $(INCLUDES) +CC = gcc -.c.s: - $(CC) $(CFLAGS) -S $< -.c.o: - $(CC) $(CFLAGS) -c $< -.s.o: - $(AS) -o $*.o $< +all: kernel + make -C util + make -C kernel/src ncpfs.o + cp kernel/src/ncpfs.o bin -OBJS= dir.o inode.o file.o sock.o ioctl.o ncplib.o - -all: ncpfs.o ncpmount ncptest - -ncpfs.o: $(OBJS) - $(LD) -r -o ncpfs.o $(OBJS) - -ncplib.o: ncplib.c ncplib.h - $(CC) $(CFLAGS) -finline-functions -c $< - -ncpmount: ncpmount.o ncplib_user.o nwcrypt.o - gcc -o ncpmount ncpmount.o ncplib_user.o nwcrypt.o - -ncpmount.o: ncpmount.c - gcc -c ncpmount.c -Wall -I. -g - -ncptest: ncptest.o ncplib_user.o nwcrypt.o - gcc -o ncptest ncptest.o ncplib_user.o nwcrypt.o - -ncptest.o: ncptest.c - gcc -c ncptest.c -Wall -I. -g - -ncplib_user.o: ncplib_user.c ncplib_user.h - gcc -c ncplib_user.c -Wall -I. -g - -nwcrypt.o: nwcrypt.c - gcc -c -O2 -Wall nwcrypt.c +kernel: + rm -f kernel + ln -s kernel-$(KERNEL) kernel dep: $(CPP) -M $(INCLUDES) *.c > .depend clean: - rm -f *.o *~ + rm -f kernel + rm -f `find . -type f -name '*.o' -print` + rm -f `find . -type f -name '*~' -print` + rm -f `find . -type f -name '.depend' -print` + rm -f `find . -type f -name '*.out' -print` + realclean: clean - rm -f ncpmount ncptest .depend $(DISTFILE) *.out + rm -fr bin/* ncpfs.tgz util/mnt + make -C util realclean modules: ncpfs.o SRCPATH=$(shell pwd) SRCDIR=$(shell basename $(SRCPATH)) DISTFILE=$(SRCDIR).tgz -BACKUPFILE=ncpfs01.tgz -HOME=/home/me - -backup: - (rm -f $(DISTFILE); cd ..; tar cvf - $(SRCDIR) | gzip -1 \ - > $(HOME)/tarz/backup/$(BACKUPFILE)) - (cd $(HOME)/tarz/backup; ls -l $(BACKUPFILE); mcopy $(BACKUPFILE) a:) dist: realclean - rm -fr mnt (cd ..; \ tar cvf - $(SRCDIR) | \ gzip -9 > $(DISTFILE); \ diff --git a/README b/README index 9704108..4b218d3 100644 --- a/README +++ b/README @@ -1,22 +1,22 @@ -This is version 0.5 of ncpfs, a free NetWare client for Linux. For me -it works with 1.3.39, although this version has severe problems with -the socket layer. Your connection will block after you have sent 64k -of requests to the server. Alan Cox told me he would like to have that -fixed in 1.3.42 or so. +This is version 0.6 of ncpfs, a free NetWare client for Linux. You +need at least kernel 1.3.44 for this version. It does NOT work with +any lower one, especially not with version 1.2.x. -I know that this piece of software is VERY incomplete, I have to -apologize for that. But I thought I should make it publically -available, because I have tried to ask several people about the legal -status of the code. I did not get very satisfying answers. So I publish -ncpfs to open it for criticism. If nobody complains, I will go on -working. +To install ncpfs, just type 'make'. After that, you find the +neccessary kernel module and the mounting tools in ./bin. Type 'insmod +ncpfs.o' and then 'ncpmount server mount-point'. For further +information, please look at the manual pages in ./man. -To install ncpfs, just type 'make', 'insmod ncpfs.o' and then -'ncpmount server mount-point'. +Please note that your IPX system has to be configured correctly. If +you want to take the 'Plug-and-Play' route, you can simply say +'ipx_configure --auto_interface=on --auto_primary=on'. If ncpmount +does not work immediately, you should wait for about 1 minute and try +again. In that period, an IPX packet should have passed by and your +network interface should have configured itself automatically. -Please note that your IPX system has to be configured correctly. There -has to be a route to the internal network of your server. Please see -the file start_ipx for an example. +If all that does not work and you want to do the configuration by +hand, note that there has to be a route to the internal network of +your server. Please see the file util/start_ipx for an example. I use tools written by Greg Page, Caldera. I hope I did not do too much harm to their business. For your convenience I included the file diff --git a/ncp.h b/kernel-1.3/linux/ncp.h similarity index 85% rename from ncp.h rename to kernel-1.3/linux/ncp.h index 36eb85b..83ffefd 100644 --- a/ncp.h +++ b/kernel-1.3/linux/ncp.h @@ -45,6 +45,7 @@ struct ncp_reply_header { #define NCP_BINDERY_USER (0x0001) #define NCP_BINDERY_UGROUP (0x0002) #define NCP_BINDERY_PQUEUE (0x0003) +#define NCP_BINDERY_FSERVER (0x0004) #define NCP_BINDERY_NAME_LEN (48) struct ncp_bindery_object { __u32 object_id; @@ -177,6 +178,39 @@ struct nw_info_struct __u8 entryName[256] __attribute__ ((packed)); }; +/* modify mask - use with MODIFY_DOS_INFO structure */ +#define DM_ATTRIBUTES (0x0002L) +#define DM_CREATE_DATE (0x0004L) +#define DM_CREATE_TIME (0x0008L) +#define DM_CREATOR_ID (0x0010L) +#define DM_ARCHIVE_DATE (0x0020L) +#define DM_ARCHIVE_TIME (0x0040L) +#define DM_ARCHIVER_ID (0x0080L) +#define DM_MODIFY_DATE (0x0100L) +#define DM_MODIFY_TIME (0x0200L) +#define DM_MODIFIER_ID (0x0400L) +#define DM_LAST_ACCESS_DATE (0x0800L) +#define DM_INHERITED_RIGHTS_MASK (0x1000L) +#define DM_MAXIMUM_SPACE (0x2000L) + +struct nw_modify_dos_info +{ + __u32 attributes __attribute__ ((packed)); + __u16 creationDate __attribute__ ((packed)); + __u16 creationTime __attribute__ ((packed)); + __u32 creatorID __attribute__ ((packed)); + __u16 modifyDate __attribute__ ((packed)); + __u16 modifyTime __attribute__ ((packed)); + __u32 modifierID __attribute__ ((packed)); + __u16 archiveDate __attribute__ ((packed)); + __u16 archiveTime __attribute__ ((packed)); + __u32 archiverID __attribute__ ((packed)); + __u16 lastAccessDate __attribute__ ((packed)); + __u16 inheritanceGrantMask __attribute__ ((packed)); + __u16 inheritanceRevokeMask __attribute__ ((packed)); + __u32 maximumSpace __attribute__ ((packed)); +}; + struct nw_file_info { struct nw_info_struct i; int opened; diff --git a/ncp_fs.h b/kernel-1.3/linux/ncp_fs.h similarity index 98% rename from ncp_fs.h rename to kernel-1.3/linux/ncp_fs.h index 6b903d1..8373185 100644 --- a/ncp_fs.h +++ b/kernel-1.3/linux/ncp_fs.h @@ -27,6 +27,7 @@ struct ncp_ioctl_request { }; #define NCP_IOC_NCPREQUEST _IOR('n', 1, unsigned char *) +#define NCP_IOC_GETMOUNTUID _IOR('u', 1, uid_t) /* * The packet size to allocate. One page should be enough. diff --git a/ncp_fs_i.h b/kernel-1.3/linux/ncp_fs_i.h similarity index 100% rename from ncp_fs_i.h rename to kernel-1.3/linux/ncp_fs_i.h diff --git a/ncp_fs_sb.h b/kernel-1.3/linux/ncp_fs_sb.h similarity index 100% rename from ncp_fs_sb.h rename to kernel-1.3/linux/ncp_fs_sb.h diff --git a/ncp_mount.h b/kernel-1.3/linux/ncp_mount.h similarity index 100% rename from ncp_mount.h rename to kernel-1.3/linux/ncp_mount.h diff --git a/kernel-1.3/src/Makefile b/kernel-1.3/src/Makefile new file mode 100644 index 0000000..ee74518 --- /dev/null +++ b/kernel-1.3/src/Makefile @@ -0,0 +1,67 @@ +# +# Makefile for the linux ncp-filesystem routines. +# + +INCLUDES = -I/usr/src/linux/include -I.. + +CFLAGS = -Wall -Wstrict-prototypes -O2 -DMODULE -fomit-frame-pointer \ + $(INCLUDES) \ +# -DDEBUG_NCP=2 -DDEBUG_NCP_MALLOC +# -DDEBUG_NCP_MALLOC + +CC = gcc -D__KERNEL__ -I. +AS = as +ARCH = i386 + +.c.s: + $(CC) $(CFLAGS) -S $< +.c.o: + $(CC) $(CFLAGS) -c $< +.s.o: + $(AS) -o $*.o $< + +OBJS= dir.o inode.o file.o sock.o ioctl.o ncplib.o + +all: ncpfs.o + +ncpfs.o: $(OBJS) + $(LD) -r -o ncpfs.o $(OBJS) + +ncplib.o: ncplib.c ncplib.h + $(CC) $(CFLAGS) -finline-functions -c $< + +dep: + $(CPP) -M $(INCLUDES) *.c > .depend + +clean: + rm -f *.o *~ + +realclean: clean + rm -f ncpmount ncptest .depend $(DISTFILE) *.out + +modules: ncpfs.o + +SRCPATH=$(shell pwd) +SRCDIR=$(shell basename $(SRCPATH)) +DISTFILE=$(SRCDIR).tgz +BACKUPFILE=ncpfs01.tgz +HOME=/home/me + +backup: + (rm -f $(DISTFILE); cd ..; tar cvf - $(SRCDIR) | gzip -1 \ + > $(HOME)/tarz/backup/$(BACKUPFILE)) + (cd $(HOME)/tarz/backup; ls -l $(BACKUPFILE); mcopy $(BACKUPFILE) a:) + +dist: realclean + rm -fr mnt + (cd ..; \ + tar cvf - $(SRCDIR) | \ + gzip -9 > $(DISTFILE); \ + mv $(DISTFILE) $(SRCDIR)) + +# +# include a dependency file if one exists +# +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/dir.c b/kernel-1.3/src/dir.c similarity index 100% rename from dir.c rename to kernel-1.3/src/dir.c diff --git a/file.c b/kernel-1.3/src/file.c similarity index 100% rename from file.c rename to kernel-1.3/src/file.c diff --git a/inode.c b/kernel-1.3/src/inode.c similarity index 93% rename from inode.c rename to kernel-1.3/src/inode.c index b34f9f5..5d1aa7c 100644 --- a/inode.c +++ b/kernel-1.3/src/inode.c @@ -357,11 +357,12 @@ ncp_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) memcpy_tofs(buf, &tmp, bufsiz); } -/* DO MORE */ static int ncp_notify_change(struct inode *inode, struct iattr *attr) { int result = 0; + int info_mask; + struct nw_modify_dos_info info; if ((result = inode_change_ok(inode, attr)) < 0) return result; @@ -379,6 +380,38 @@ ncp_notify_change(struct inode *inode, struct iattr *attr) ~(S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO)))) return -EPERM; + info_mask = 0; + memset(&info, 0, sizeof(info)); + + if ((attr->ia_valid & ATTR_CTIME) != 0) { + info_mask |= (DM_CREATE_TIME|DM_CREATE_DATE); + ncp_date_unix2dos(attr->ia_ctime, + &(info.creationTime), &(info.creationDate)); + } + + if ((attr->ia_valid & ATTR_MTIME) != 0) { + info_mask |= (DM_MODIFY_TIME|DM_MODIFY_DATE); + ncp_date_unix2dos(attr->ia_mtime, + &(info.modifyTime), &(info.modifyDate)); + } + + if ((attr->ia_valid & ATTR_ATIME) != 0) { + __u16 dummy; + info_mask |= (DM_LAST_ACCESS_DATE); + ncp_date_unix2dos(attr->ia_ctime, + &(dummy), &(info.lastAccessDate)); + } + + if (info_mask != 0) { + if ((result = + ncp_modify_file_or_subdir_dos_info(NCP_SERVER(inode), + NCP_ISTRUCT(inode), + info_mask, + &info)) != 0) { + result = -EACCES; + } + } + if ((attr->ia_valid & ATTR_SIZE) != 0) { int written; diff --git a/ioctl.c b/kernel-1.3/src/ioctl.c similarity index 78% rename from ioctl.c rename to kernel-1.3/src/ioctl.c index a1c71cb..36d8f36 100644 --- a/ioctl.c +++ b/kernel-1.3/src/ioctl.c @@ -58,9 +58,8 @@ ncp_ioctl (struct inode * inode, struct file * filp, server->has_subfunction = 0; server->current_size = request.size + sizeof(struct ncp_request_header); - memcpy_fromfs(&(server-> - packet[sizeof(struct ncp_request_header)]), - request.data, request.size); + memcpy_fromfs(server->packet, request.data, + request.size+sizeof(struct ncp_request_header)); ncp_request(server, request.function); @@ -73,6 +72,15 @@ ncp_ioctl (struct inode * inode, struct file * filp, ncp_unlock_server(server); return server->reply_size; + + case NCP_IOC_GETMOUNTUID: + if ((result = verify_area(VERIFY_WRITE, (uid_t*) arg, + sizeof(uid_t))) != 0) { + return result; + } + put_fs_word(NCP_SERVER(inode)->m.mounted_uid, (uid_t*) arg); + return 0; + default: return -EINVAL; } diff --git a/ncplib.c b/kernel-1.3/src/ncplib.c similarity index 94% rename from ncplib.c rename to kernel-1.3/src/ncplib.c index 2192dca..7a14317 100644 --- a/ncplib.c +++ b/kernel-1.3/src/ncplib.c @@ -1,5 +1,4 @@ #include "ncplib.h" -#include "nwcrypt.h" typedef __u8 byte; typedef __u16 word; @@ -41,7 +40,7 @@ ncp_add_dword(struct ncp_server *server, dword x) } static void -ncp_add_mem(struct ncp_server *server, const char *source, int size) +ncp_add_mem(struct ncp_server *server, const void *source, int size) { assert_server_locked(server); memcpy(&(server->packet[server->current_size]), source, size); @@ -318,6 +317,34 @@ ncp_do_lookup(struct ncp_server *server, return 0; } +int +ncp_modify_file_or_subdir_dos_info(struct ncp_server *server, + struct nw_info_struct *file, + __u32 info_mask, + struct nw_modify_dos_info *info) +{ + int result; + + ncp_init_request(server); + ncp_add_byte(server, 7); /* subfunction */ + ncp_add_byte(server, 0); /* dos name space */ + ncp_add_byte(server, 0); /* reserved */ + ncp_add_word(server, 0x8006); /* search attribs: all */ + + ncp_add_dword(server, info_mask); + ncp_add_mem(server, info, sizeof(*info)); + ncp_add_handle_path(server, file->volNumber, + file->DosDirNum, 1, NULL); + + if ((result = ncp_request(server, 87)) != 0) { + ncp_unlock_server(server); + return result; + } + + ncp_unlock_server(server); + return 0; +} + int ncp_del_file_or_subdir(struct ncp_server *server, struct nw_info_struct *dir, char *name) @@ -441,7 +468,7 @@ ncp_search_for_file_or_subdir(struct ncp_server *server, ncp_add_byte(server, 0); /* data stream (???) */ ncp_add_word(server, 0xffff); /* Search attribs */ ncp_add_dword(server, RIM_ALL); /* return info mask */ - ncp_add_mem(server, (unsigned char *)seq, 9); + ncp_add_mem(server, seq, 9); ncp_add_byte(server, 2); /* 2 byte pattern */ ncp_add_byte(server, 0xff); /* following is a wildcard */ ncp_add_byte(server, '*'); diff --git a/ncplib.h b/kernel-1.3/src/ncplib.h similarity index 95% rename from ncplib.h rename to kernel-1.3/src/ncplib.h index 86e52c8..3278cdf 100644 --- a/ncplib.h +++ b/kernel-1.3/src/ncplib.h @@ -119,6 +119,12 @@ ncp_do_lookup(struct ncp_server *server, char *path, /* may only be one component */ struct nw_info_struct *target); +int +ncp_modify_file_or_subdir_dos_info(struct ncp_server *server, + struct nw_info_struct *file, + __u32 info_mask, + struct nw_modify_dos_info *info); + int ncp_del_file_or_subdir(struct ncp_server *server, struct nw_info_struct *dir, char *name); diff --git a/sock.c b/kernel-1.3/src/sock.c similarity index 100% rename from sock.c rename to kernel-1.3/src/sock.c diff --git a/linux b/linux deleted file mode 120000 index 945c9b4..0000000 --- a/linux +++ /dev/null @@ -1 +0,0 @@ -. \ No newline at end of file diff --git a/man/ipx_configure.8 b/man/ipx_configure.8 new file mode 100644 index 0000000..8bb1c96 --- /dev/null +++ b/man/ipx_configure.8 @@ -0,0 +1,42 @@ +.TH IPX_CONFIGURE 8 "IPX Utilities" "Caldera, Inc." +.SH NAME +ipx_configure \- query/configure IPX behavior +.SH SYNOPSIS +.B ipx_configure +[\-\-help] +[\-\-auto_interface=[on|off]] +[\-\-auto_primary=[on|off]] +.SH DESCRIPTION +.B ipx_configure +queries or configures IPX behavior with respect to automatic IPX +interface detection. IPX can be configured to automatically create +interfaces as they are detected. It can also be configured to +automatically select a primary interface when none is explicitly +selected. By default, it is configured to +.B NOT +have this behavior. +Without arguments, +.B ipx_configure +returns the current configuration state. The behavior with +arguments is described in the section +.B OPTIONS. +.SS OPTIONS +.TP +.I "\-\-auto_interface=[on|off]" +This argument either turns on or off the behavior of automatically creating +interfaces. +.TP +.I "\-\-auto_primary=[on|off]" +This argument either turns on or off the behavior of automatically selecting +a primary interface. +.TP +.I "\-\-help" +Print out information about utility. +.SH FILES +.I /proc/net/ipx_interface +.SH BUGS +This functionality really belongs in +.B +ifconfig(8). +.SH AUTHOR +Greg Page diff --git a/man/ncpmount.8 b/man/ncpmount.8 new file mode 100644 index 0000000..a168c3c --- /dev/null +++ b/man/ncpmount.8 @@ -0,0 +1,190 @@ +.TH NCPMOUNT 8 25/11/1995 ncpmount ncpmount +.SH NAME +ncpmount \- mount program for ncpfs +.SH SYNOPSIS +.B ncpmount +.B server mount-point +[ +.B -h +] [ +.B -n +.I +] [ +.B -P +.I password +] [ +.B -C +] [ +.B -s +.I server name +] [ +.B -c +.I client name +] [ +.B -U +.I user name +] [ +.B -u +.I uid +] [ +.B -g +.I gid +] [ +.B -f +.I file mode +] [ +.B -d +.I dir mode +] + +.SH DESCRIPTION +This program is an interface to the NCP filesystem. + +.B ncpfs +is a filesystem which understands the NCP protocol. This is the +protocol Novell NetWare clients use to talk to NetWare servers. ncpfs +was inspired by +.B lwared, +a free NetWare emulator for Linux written by Ales Dryak. See +ftp://klokan.sh.cvut.cz/pub/linux for this very intersting program. + +.SH OPTIONS +.B server +.RS 3 +.B server +is the name of the server you want to use on the server. +.RE + +.B mount-point +.RS 3 +.B mount-point +is the directory you want to mount the filesystem over. It's the same +as in the normal mount command. + +If the real uid of the caller is not root, +.B ncpmount +checks whether the user is allowed to mount a filesystem on the +mount-point. So it should be safe to make +.B ncpmount + setuid root. The filesystem stores the uid of the user who called +ncpmount. So +.B ncpumount +can check whether the caller is allowed to unmount the filesystem. +.RE + +.B -h +.RS 3 +.B -h +is used to print out a short help text. +.RE + +.B -C +.RS 3 +By default, passwords are converted to uppercase before they are sent +to the server, because most servers require this. You can turn off +this conversion by +.B -C. +.RE + +.B -n +.RS 3 +.B -n +should be given to mount shares which do not require a password to log in. +.RE + +.B -P +.I password +.RS 3 +You may want to give the password required by the server on the +command line. You should be careful to use passwords in scripts. + +If neither +.B -n +nor +.B -P +are given, ncpmount prompts for a password. This makes it difficult to +use in scripts such as /etc/rc. But that's not ncpmount's fault, but a +general problem with the fact that you need a password on every +login. If anybody has a satisfying solution to this problem, please +tell me. +.RE + +.B -U +.I user name +.RS 3 +If the user name your NetWare administrator gave to you differs +from your unix user-id, you should use +.B -U +to tell the server about you NetWare user name. +.RE + +.B -u +.I uid, +.B -g +.I gid +.RS 3 +Currently I did not implement a mapping from NetWare users/groups to +unix users/groups. Unix requires that each file has an owner +and a group it belongs to. With +.B -u +and +.B -g +you can tell ncpmount which id's it should assign to the files in the +mounted direcory. + +The defaults for these values are the current uid and gid. +.RE + +.B -f +.I file mode, +.B -d +.I dir mode +.RS 3 +Like +.B -u +and +.B -g, +these options are also used to cover deficiencies in the +implementation of ncpfs. I did not implement a scheme to map NetWare +permissions to unix permissions. So ncpmount has to be told which +permissions it should assign to the mounted files and direcories. The +values have to be given as octal numbers. The default values are taken +from the current umask, where the file mode is the current umask, and +the dir mode adds execute permissions where the file mode gives read +permissions. + +Note that these permissions can differ from the rights the server +gives to us. If you do not have write permissions on the server, you +can very well choose a file mode that tells that you have. This +certainly cannot override the restrictions imposed by the server. +.RE + +.SH NOTES +If you have difficulties in mounting, please make sure that you have configured your ipx subsystem correctly. It is especially important that there is a route to the internal network of your server. + +.SH ENVIRONMENT VARIABLES +.B USER / LOGNAME +.RS 3 +The variables USER or LOGNAME may contain the username of the person +using the client. USER is tried first. If it's emtpy, LOGNAME is +tried. +.RE + +.SH DIAGNOSTICS + +Most diagnostics issued by ncpfs are logged by syslogd. Normally +nothing is printed, only error situations are logged there. + +.SH SEE ALSO +.B syslogd(8), ncpumount(8) + +.SH CREDITS +ncpfs would not have been possible without lwared, written by Ales +Dryak (A.Dryak@sh.cvut.cz). + +The encryption code was taken from Dr. Dobbs's Journal 11/93. There +Pawel Szczerbina described it in an article on NCP. + +The ncpfs code was initially hacked from smbfs by Volker Lendecke +(lendecke@namu01.gwdg.de). smbfs was put together by Paal-Kr. Engstad +(pke@engstad.ingok.hitos.no) and later polished by Volker. diff --git a/man/ncpumount.8 b/man/ncpumount.8 new file mode 100644 index 0000000..6724407 --- /dev/null +++ b/man/ncpumount.8 @@ -0,0 +1,28 @@ +.TH NCPUMOUNT 8 25/11/1995 ncpumount ncpumount +.SH NAME +ncpumount \- umount for normal users +.SH SYNOPSIS +.B ncpumount +.B mount-point + +.SH DESCRIPTION +With this program, normal users can unmount ncp-filesystems, provided +that it is suid root. + +.B ncpumount +has been written to give normal linux-users more control over their +resources. It is safe to install this program suid root, because only +the user who has mounted a filesystem is allowed to unmount it again. + +For root it is not necessary to use ncpumount. The normal umount +program works perfectly well, but it would certainly be problematic to +make umount setuid root. + +.SH OPTIONS +.B mount-point +.RS 3 +.B mount-point +is the directory you want to unmount. + +.SH SEE ALSO +.B ncpmount(8) diff --git a/ncpfs-0.5.lsm b/ncpfs-0.6.lsm similarity index 73% rename from ncpfs-0.5.lsm rename to ncpfs-0.6.lsm index b30c36f..71cadf8 100644 --- a/ncpfs-0.5.lsm +++ b/ncpfs-0.6.lsm @@ -1,14 +1,14 @@ Begin3 Title: ncpfs -Version: 0.5 -Entered-date: 17. November 1995 +Version: 0.6 +Entered-date: 25. November 1995 Description: With ncpfs you can mount volumes of your novell server under Linux. Keywords: filesystem kernel ncp novell netware Author: lendecke@namu01.gwdg.de (Volker Lendecke) Maintained-by: lendecke@namu01.gwdg.de (Volker Lendecke) Primary-site: linux01.gwdg.de:/pub/ncpfs - ~50k ncpfs-0.5.tgz - ~ 1k ncpfs-0.5.lsm + ~59k ncpfs-0.6.tgz + ~ 1k ncpfs-0.6.lsm Copying-policy: GPL End diff --git a/util/Makefile b/util/Makefile new file mode 100644 index 0000000..9e4c9de --- /dev/null +++ b/util/Makefile @@ -0,0 +1,46 @@ +# +# Makefile for the linux ncp-filesystem routines. +# + +INCLUDES = -I/usr/src/linux/include -I../kernel +BINDIR = ../bin +UTILS = ncpmount ncpumount ncptest ipx_configure + +CFLAGS = -Wall $(INCLUDES) -g +CC = gcc + +all: $(UTILS) + +ncpmount: ncpmount.o ncplib_user.o nwcrypt.o + $(CC) -o ncpmount ncpmount.o ncplib_user.o nwcrypt.o + cp ncpmount $(BINDIR) + +ncpumount: ncpumount.o ncplib_user.o nwcrypt.o + $(CC) -o ncpumount ncpumount.o ncplib_user.o nwcrypt.o + cp ncpumount $(BINDIR) + +ipx_configure: ipx_configure.c + $(CC) $(CFLAGS) ipx_configure.c -o ipx_configure + cp ipx_configure $(BINDIR) + +ncptest: ncptest.o ncplib_user.o nwcrypt.o + $(CC) -o ncptest ncptest.o ncplib_user.o nwcrypt.o + +nwcrypt.o: nwcrypt.c + $(CC) -c -O3 -Wall nwcrypt.c + +dep: + $(CPP) -M $(INCLUDES) *.c > .depend + +clean: + rm -f *.o *~ + +realclean: clean + rm -f $(UTILS) .depend $(DISTFILE) + +# +# include a dependency file if one exists +# +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/ipx.tar b/util/ipx.tar similarity index 100% rename from ipx.tar rename to util/ipx.tar diff --git a/util/ipx_configure.c b/util/ipx_configure.c new file mode 100644 index 0000000..c2b00ca --- /dev/null +++ b/util/ipx_configure.c @@ -0,0 +1,125 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct option options[] = { + { "auto_primary", required_argument, NULL, 1 }, + { "auto_interface", required_argument, NULL, 2 }, + { "help", no_argument, NULL, 3}, + { NULL, 0, NULL, 0 } +}; + +char *progname; + +void +usage(void) +{ + fprintf(stderr, + "Usage: %s --auto_primary=[on|off]\n\ +Usage: %s --auto_interface=[on|off]\n\ +Usage: %s --help\n\ +Usage: %s\n", progname, progname, progname, progname); +} + +int +map_string_to_bool(char *optarg) +{ + if ((strcasecmp(optarg, "ON") == 0) || + (strcasecmp(optarg, "TRUE") == 0) || + (strcasecmp(optarg, "SET") == 0) || + (strcasecmp(optarg, "YES") == 0)) { + return 1; + } else if ((strcasecmp(optarg, "OFF") == 0) || + (strcasecmp(optarg, "FALSE") == 0) || + (strcasecmp(optarg, "CLEAR") == 0) || + (strcasecmp(optarg, "NO") == 0)) { + return 0; + } + + return -1; +} + +int +main(int argc, char **argv) +{ + int s; + int result; + char errmsg[80]; + char val; + int option_index = 0; + int got_auto_pri = 0; + int got_auto_itf = 0; + ipx_config_data data; + + progname = argv[0]; + + s = socket(AF_IPX, SOCK_DGRAM, AF_IPX); + if (s < 0) { + sprintf(errmsg, "%s: socket", progname); + perror(errmsg); + exit(-1); + } + + sprintf(errmsg, "%s: ioctl", progname); + while ((result = getopt_long(argc, argv, "", options, + &option_index)) != -1) { + switch (result) { + case 1: + if (got_auto_pri) + break; + got_auto_pri++; + + val = map_string_to_bool(optarg); + if (val < 0) { + usage(); + exit(-1); + } + + result = ioctl(s, SIOCAIPXPRISLT, &val); + if (result < 0) { + perror(errmsg); + exit(-1); + } + break; + case 2: + if (got_auto_itf) + break; + got_auto_itf++; + + val = map_string_to_bool(optarg); + if (val < 0) { + usage(); + exit(-1); + } + + result = ioctl(s, SIOCAIPXITFCRT, &val); + if (result < 0) { + perror(errmsg); + exit(-1); + } + break; + case 3: + usage(); + break; + } + } + result = ioctl(s, SIOCIPXCFGDATA, &data); + if (result < 0) { + perror(errmsg); + exit(-1); + } + if (argc == 1) { + fprintf(stdout, "Auto Primary Select is %s\n\ +Auto Interface Create is %s\n", + (data.ipxcfg_auto_select_primary) ? "ON" : "OFF", + (data.ipxcfg_auto_create_interfaces) ? "ON" : "OFF"); + } + exit(0); +} + diff --git a/ipxutil.h b/util/ipxutil.h similarity index 70% rename from ipxutil.h rename to util/ipxutil.h index 6459bbf..c9c56ec 100644 --- a/ipxutil.h +++ b/util/ipxutil.h @@ -27,10 +27,29 @@ #include #include +#define IPX_RIP_PTYPE (0x01) #define IPX_SAP_PTYPE (0x04) #define IPX_SAP_PORT (0x0452) +#define IPX_RIP_PORT (0x0453) -#define IPX_SAP_FILE_SERVER (0x004) +#define IPX_SAP_GENERAL_QUERY (0x0001) +#define IPX_SAP_GENERAL_RESPONSE (0x0002) +#define IPX_SAP_NEAREST_QUERY (0x0003) +#define IPX_SAP_NEAREST_RESPONSE (0x0004) + +#define IPX_SAP_FILE_SERVER (0x0004) + +#define IPX_RIP_REQUEST (0x1) +#define IPX_RIP_RESPONSE (0x2) + +struct ipx_rip_packet { + __u16 operation __attribute__ ((packed)); + struct ipx_rt_def { + __u32 network __attribute__ ((packed)); + __u16 hops __attribute__ ((packed)); + __u16 ticks __attribute__ ((packed)); + } rt[1] __attribute__ ((packed)); +}; #define IPX_BROADCAST_NODE "\xff\xff\xff\xff\xff\xff" #define IPX_THIS_NODE "\0\0\0\0\0\0" diff --git a/ncplib_user.c b/util/ncplib_user.c similarity index 74% rename from ncplib_user.c rename to util/ncplib_user.c index 55f9f2c..234d970 100644 --- a/ncplib_user.c +++ b/util/ncplib_user.c @@ -6,9 +6,17 @@ typedef __u16 word; typedef __u32 dword; #include +#include #include #include #include +#include +#include +#include +#include +#include +#include +#include "ipxutil.h" #define ncp_printf printf @@ -38,10 +46,10 @@ ncp_unlock_server(struct ncp_server *server) } static int -ncp_request(struct ncp_server *server, int function) { +ncp_ioctl_request(struct ncp_server *server, int function) { struct ncp_reply_header *reply - = (struct ncp_reply_header *)(server->packet); + = (struct ncp_reply_header *)(server->ncp_data); struct ncp_ioctl_request request; int result; @@ -53,7 +61,7 @@ ncp_request(struct ncp_server *server, int function) { request.function = function; request.size = server->current_size; - request.data = server->packet; + request.data = server->ncp_data; if ((result = ioctl(server->mount_fid, NCP_IOC_NCPREQUEST, &request)) < 0) { @@ -70,6 +78,347 @@ ncp_request(struct ncp_server *server, int function) { return result; } +static int +do_ncp_call(struct ncp_server *server, int request_size) +{ + struct ncp_request_header request = + *((struct ncp_request_header *)(&(server->ncp_data))); + + fd_set rd, wr, ex; + struct timeval tv; + + int result; + int retries = 3; + + while (retries > 0) { + retries -= 1; + + result = sendto(server->ncp_sock, server->ncp_data, + request_size, + 0, (struct sockaddr *)&(server->addr), + sizeof(server->addr)); + + if (result < 0) { + return result; + } + + re_select: + FD_ZERO(&rd); FD_ZERO(&wr); FD_ZERO(&ex); + FD_SET(server->ncp_sock, &rd); + + tv.tv_sec = 3; + tv.tv_usec = 0; + + if (select(server->ncp_sock+1, &rd, &wr, &ex, &tv) == -1) { + perror("select"); + return -1; + } + + if (FD_ISSET(server->ncp_sock, &rd)) { + int len = recv(server->ncp_sock, + server->ncp_data, NCP_PACKET_SIZE, + 0); + struct ncp_reply_header *r = + (struct ncp_reply_header *)&(server->ncp_data); + + if ( (len == sizeof(*r)) + && (r->type == NCP_POSITIVE_ACK)) { + goto re_select; + } + if ( (len >= sizeof(*r)) + && (r->type == NCP_REPLY) + && ( (request.type == NCP_ALLOC_SLOT_REQUEST) + || ( (r->sequence == request.sequence) + && (r->conn_low == request.conn_low) + && (r->conn_high == request.conn_high)))) { + server->reply_size = len; + break; + } + } + } + return 0; +} + +static int +ncp_user_request(struct ncp_server *server, int function) +{ + struct ncp_request_header *h = + (struct ncp_request_header *)&(server->ncp_data); + struct ncp_reply_header *r = + (struct ncp_reply_header *)&(server->ncp_data); + + int result; + + assert_server_locked(server); + + if (server->has_subfunction != 0) { + *(__u16 *)(server->packet) = server->current_size - 2; + } + + h->type = NCP_REQUEST; + + server->sequence += 1; + h->sequence = server->sequence; + h->conn_low = (server->connection) & 0xff; + h->conn_high = ((server->connection) & 0xff00) >> 8; + h->task = 1; + h->function = function; + + if (do_ncp_call(server, server->current_size + sizeof(*h)) != 0) { + return -1; + } + + server->completion = r->completion_code; + server->conn_status = r->connection_state; + server->ncp_reply_size = + server->reply_size - sizeof(struct ncp_reply_header); + + result = r->completion_code; + + if (result != 0) { + ncp_printf("ncp_completion_code: %d\n", result); + } + return result; +} + +int +install_wdog(struct ncp_server *server) +{ + int parent_pid = getpid(); + int pid; + int sock = server->wdog_sock; + + fd_set rd, wr, ex; + struct timeval tv; + char buf[1024]; + struct sockaddr_ipx sender; + int sizeofaddr = sizeof(struct sockaddr_ipx); + int pktsize; + + + if ((pid = fork()) < 0) { + return -1; + } + + if (pid != 0) { + /* Parent, should go on as usual */ + server->wdog_pid = pid; + return 0; + } + + while (1) { + + FD_ZERO(&rd); FD_ZERO(&wr); FD_ZERO(&ex); + FD_SET(sock, &rd); + + /* every 120 seconds we look if our parent is + still alive */ + tv.tv_sec = 120; + tv.tv_usec = 0; + + if (select(sock+1, &rd, &wr, &ex, &tv) == -1) { + continue; + } + + if (getppid() != parent_pid) { + /* our parent has died, so nothing to do + anymore */ + exit(0); + } + + if (FD_ISSET(sock, &rd)) { + pktsize = recvfrom(sock, buf, sizeof(buf), 0, + (struct sockaddr *)&sender, + &sizeofaddr); + if (pktsize < 0) { + perror("recvfrom"); + continue; + } + if ( (pktsize != 2) + || (buf[1] != '?')) { + continue; + } + buf[1] = 'Y'; + pktsize = sendto(sock, buf, 2, 0, + (struct sockaddr *)&sender, + sizeof(sender)); + if (pktsize < 0) { + perror("send"); + } + } + } +} + +int +ncp_connect(struct ncp_server *server) +{ + struct ncp_request_header *h = + (struct ncp_request_header *)&(server->ncp_data); + + struct sockaddr_ipx addr; + int len = sizeof(struct sockaddr_ipx); + + int ncp_sock, wdog_sock; + int ncp_port, wdog_port; + + server->is_connected = NOT_CONNECTED; + + ncp_sock = socket(AF_IPX, SOCK_DGRAM, PF_IPX); + if (ncp_sock == -1) { + perror("open ncp socket"); + return -1; + } + + wdog_sock = socket(AF_IPX, SOCK_DGRAM, PF_IPX); + if (wdog_sock == -1) { + perror("open wdog socket"); + close(ncp_sock); + return -1; + } + + addr.sipx_family = AF_IPX; + addr.sipx_network = htonl(0x0); + ipx_assign_node(addr.sipx_node, IPX_THIS_NODE); + addr.sipx_port = htons(0x0); + addr.sipx_type = NCP_PTYPE; + + if (bind(ncp_sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + perror("bind ncp socket"); + close(ncp_sock); close(wdog_sock); + return -1; + } + + if (bind(wdog_sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + perror("bind wdog socket"); + close(ncp_sock); close(wdog_sock); + return -1; + } + + if ( (getsockname(ncp_sock, (struct sockaddr *)&addr, &len) != 0) + || (len != sizeof(struct sockaddr_ipx))) { + perror("getsockname ncp socket"); + close(ncp_sock); close(wdog_sock); + return -1; + } + ncp_port = ntohs(addr.sipx_port); + + if ( (getsockname(wdog_sock, (struct sockaddr *)&addr, &len) != 0) + || (len != sizeof(struct sockaddr_ipx))) { + perror("getsockname wdog socket"); + close(wdog_sock); close(wdog_sock); + return -1; + } + wdog_port = ntohs(addr.sipx_port); + + if (wdog_port != ncp_port+1) { + fprintf(stderr, "did not alloc 2 consecutive ports\n"); + close(ncp_sock); close(wdog_sock); + return -1; + } + + server->ncp_sock = ncp_sock; + server->wdog_sock = wdog_sock; + + h->type = NCP_ALLOC_SLOT_REQUEST; + + server->sequence = 0; + h->sequence = server->sequence; + h->conn_low = 0xff; + h->conn_high = 0xff; + h->task = 1; + h->function = 0; + + if (do_ncp_call(server, sizeof(*h)) != 0) { + int saved_errno = errno; + close(ncp_sock); close(wdog_sock); + errno = saved_errno; + return -1; + } + + install_wdog(server); + + server->sequence = 0; + server->connection = h->conn_low + (h->conn_high * 256); + + server->is_connected = CONN_SOCKET; + + return 0; +} + +static int +ncp_user_disconnect(struct ncp_server *server) +{ + struct ncp_request_header *h + = (struct ncp_request_header *)(server->ncp_data); + int result; + + h->type = NCP_DEALLOC_SLOT_REQUEST; + + server->sequence += 1; + h->sequence = server->sequence; + h->conn_low = (server->connection) & 0xff; + h->conn_high = ((server->connection) & 0xff00) >> 8; + h->task = 1; + h->function = 0; + + if ((result = do_ncp_call(server, sizeof(*h))) != 0) { + return result; + } + + close(server->ncp_sock); + close(server->wdog_sock); + kill(server->wdog_pid, SIGTERM); + wait(NULL); + return 0; +} + + +int +ncp_disconnect(struct ncp_server *server) +{ + int result = -1; + + switch (server->is_connected) { + case CONN_MOUNTED: + result = close(server->mount_fid); + case CONN_SOCKET: + result = ncp_user_disconnect(server); + default: + } + if (result >= 0) { + server->is_connected = NOT_CONNECTED; + } + return result; +} + + +int +ncp_connect_mount(struct ncp_server *server, const char *mount_point) +{ + server->mount_fid = open(mount_point, O_RDONLY, 0); + + if (server->mount_fid == -1) { + return -1; + + } + + server->is_connected = CONN_MOUNTED; + return 0; +} + +static int +ncp_request(struct ncp_server *server, int function) +{ + switch (server->is_connected) { + case CONN_MOUNTED: + return ncp_ioctl_request(server, function); + case CONN_SOCKET: + return ncp_user_request(server, function); + default: + } + return -ENOTCONN; +} + static inline int min(int a, int b) { if (acurrent_size = 0; - server->packet = server->ncp_data; + server->packet = + &(server->ncp_data[sizeof(struct ncp_request_header)]); server->has_subfunction = 0; } @@ -160,7 +510,7 @@ ncp_init_request_s(struct ncp_server *server, int subfunction) static char * ncp_reply_data(struct ncp_server *server, int offset) { - return &(server->packet[sizeof(struct ncp_reply_header) + offset]); + return &(server->ncp_data[sizeof(struct ncp_reply_header) + offset]); } static byte @@ -813,6 +1163,36 @@ ncp_do_lookup(struct ncp_server *server, return 0; } +int +ncp_modify_file_or_subdir_dos_info(struct ncp_server *server, + struct nw_info_struct *file, + __u32 info_mask, + struct nw_modify_dos_info *info) +{ + int result; + + ncp_init_request(server); + ncp_add_byte(server, 7); /* subfunction */ + ncp_add_byte(server, 0); /* dos name space */ + ncp_add_byte(server, 0); /* reserved */ + ncp_add_word(server, 0x8006); /* search attribs: all */ + + ncp_add_dword(server, info_mask); + ncp_add_mem(server, info, sizeof(*info)); + ncp_add_handle_path(server, file->volNumber, + file->DosDirNum, 1, NULL); + + if ((result = ncp_request(server, 87)) != 0) { + ncp_unlock_server(server); + return result; + } + + ncp_unlock_server(server); + return 0; +} + + + int ncp_del_file_or_subdir(struct ncp_server *server, struct nw_info_struct *dir, char *name) diff --git a/ncplib_user.h b/util/ncplib_user.h similarity index 87% rename from ncplib_user.h rename to util/ncplib_user.h index a26441c..6541cd2 100644 --- a/ncplib_user.h +++ b/util/ncplib_user.h @@ -4,21 +4,50 @@ #include #include #include +#include + +enum connect_state { + NOT_CONNECTED = 0, + CONN_MOUNTED, + CONN_SOCKET +}; struct ncp_server { int current_size; int has_subfunction; - int mount_fid; int silent; int ncp_reply_size; char *packet; int lock; + enum connect_state is_connected; + + int mount_fid; + + struct sockaddr_ipx addr; + int ncp_sock; + int wdog_sock; + int wdog_pid; + __u8 sequence; + __u16 connection; + int completion; + int conn_status; + int reply_size; + char ncp_data[NCP_PACKET_SIZE]; }; #include +int +ncp_connect_mount(struct ncp_server *server, const char *mount_point); + +int +ncp_connect(struct ncp_server *server); + +int +ncp_disconnect(struct ncp_server *server); + int ncp_negotiate_buffersize(struct ncp_server *server, int size, int *target); @@ -128,10 +157,17 @@ ncp_do_lookup(struct ncp_server *server, char *path, /* may only be one component */ struct nw_info_struct *target); +int +ncp_modify_file_or_subdir_dos_info(struct ncp_server *server, + struct nw_info_struct *file, + __u32 info_mask, + struct nw_modify_dos_info *info); + int ncp_del_file_or_subdir(struct ncp_server *server, struct nw_info_struct *dir, char *name); + int ncp_open_create_file_or_subdir(struct ncp_server *server, struct nw_info_struct *dir, char *name, diff --git a/ncpmount.c b/util/ncpmount.c similarity index 77% rename from ncpmount.c rename to util/ncpmount.c index d40ba91..bf7f173 100644 --- a/ncpmount.c +++ b/util/ncpmount.c @@ -28,6 +28,8 @@ extern pid_t waitpid(pid_t, int *, int); #include #include #include +#include +#include #include #include @@ -148,6 +150,134 @@ ipx_print_saddr(struct sockaddr_ipx* sipx) ipx_fprint_saddr(stdout,sipx); } +static int +ipx_make_reachable(__u32 network) +{ + struct rtentry rt_def; + /* Router */ + struct sockaddr_ipx *sr = (struct sockaddr_ipx *)&rt_def.rt_gateway; + /* Target */ + struct sockaddr_ipx *st = (struct sockaddr_ipx *)&rt_def.rt_dst; + + fd_set rd, wr, ex; + struct timeval tv; + + struct ipx_rip_packet rip; + struct sockaddr_ipx addr; + int addrlen; + int sock; + int opt; + int res=-1; + int i; + + sock = socket(AF_IPX, SOCK_DGRAM, PF_IPX); + + if (sock == -1) { + return -1; + } + + opt=1; + /* Permit broadcast output */ + if(setsockopt(sock,SOL_SOCKET,SO_BROADCAST, &opt,sizeof(opt))==-1) + { + perror("setsockopt"); + goto finished; + } + + memset(&addr, 0, sizeof(addr)); + addr.sipx_family=AF_IPX; + addr.sipx_network=htonl(0x0); + addr.sipx_port=htons(0x0); + addr.sipx_type=IPX_RIP_PTYPE; + + if(bind(sock,(struct sockaddr*)&addr,sizeof(addr))==-1) + { + perror("bind"); + goto finished; + } + + addr.sipx_family = AF_IPX; + addr.sipx_network = htonl(0x0); + addr.sipx_port = htons(IPX_RIP_PORT); + addr.sipx_type = IPX_RIP_PTYPE; + ipx_assign_node(addr.sipx_node, IPX_BROADCAST_NODE); + + rip.operation = htons(IPX_RIP_REQUEST); + rip.rt[0].network = htonl(network); + + if (sendto(sock, &rip, sizeof(rip), 0, + (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("sendto"); + goto finished; + } + + do + { + + FD_ZERO(&rd); FD_ZERO(&wr); FD_ZERO(&ex); + FD_SET(sock, &rd); + + tv.tv_sec = 3; + tv.tv_usec = 0; + + if (select(sock+1, &rd, &wr, &ex, &tv) == -1) + { + goto finished; + } + + if (FD_ISSET(sock, &rd)) + { + int len; + + addrlen = sizeof(struct sockaddr_ipx); + + len = recvfrom(sock, &rip, sizeof(rip), 0, + (struct sockaddr *)sr, &addrlen); + + if (len < sizeof(rip)) + { + continue; + } + } + else + { + goto finished; + } + } while (ntohs(rip.operation) != IPX_RIP_RESPONSE); + + if (rip.rt[0].network != htonl(network)) { + goto finished; + } + + rt_def.rt_flags = RTF_GATEWAY; + st->sipx_network = htonl(network); + sr->sipx_family = st->sipx_family = AF_IPX; + i = 0; + do { + res = ioctl(sock, SIOCADDRT, &rt_def); + i++; + } while ((i < 5) && (res < 0) && (errno == EAGAIN)); + + if (res != 0) { + + switch (errno) { + case ENETUNREACH: + fprintf(stderr, + "%s: Router network (%08lX) not reachable.\n", + progname, htonl(sr->sipx_network)); + break; + default: + perror("ioctl"); + break; + } + goto finished; + } + + finished: + close(sock); + return res; +} + int ipx_sap_find_server(char *name, int server_type, int timeout, struct sockaddr_ipx *result) @@ -160,16 +290,26 @@ ipx_sap_find_server(char *name, int server_type, int timeout, int name_len = strlen(name); fd_set rd, wr, ex; struct timeval tv; - int packets; + struct sap_server_ident *ident; + + struct ncp_server server; + struct nw_property prop; + struct net_address + { + __u32 network __attribute__ ((packed)); + __u8 node[6] __attribute__ ((packed)); + __u16 port __attribute__ ((packed)); + } *n_addr = (struct net_address *)∝ + if (name_len > 48) { - return -1; - } + return -1; + } sock=socket(AF_IPX,SOCK_DGRAM,PF_IPX); if (sock==-1) { - return -1; - } + return -1; + } opt=1; /* Permit broadcast output */ @@ -191,7 +331,7 @@ ipx_sap_find_server(char *name, int server_type, int timeout, goto finished; } - *(unsigned short *)data = htons(0x0001); + *(unsigned short *)data = htons(IPX_SAP_NEAREST_QUERY); *(unsigned short *)&(data[2]) = htons(server_type); memset(&ipxs, 0, sizeof(ipxs)); @@ -202,58 +342,83 @@ ipx_sap_find_server(char *name, int server_type, int timeout, ipxs.sipx_type=IPX_SAP_PTYPE; if (sendto(sock, data, 4, 0, - (struct sockaddr *)&ipxs, sizeof(ipxs)) < 0) { + (struct sockaddr *)&ipxs, sizeof(ipxs)) < 0) + { perror("sendto"); goto finished; } - packets = 10; - while (packets > 0) { + do + { - packets -= 1; - FD_ZERO(&rd); FD_ZERO(&wr); FD_ZERO(&ex); FD_SET(sock, &rd); - + tv.tv_sec = timeout; tv.tv_usec = 0; - - if (select(sock+1, &rd, &wr, &ex, &tv) == -1) { - perror("select"); + + if (select(sock+1, &rd, &wr, &ex, &tv) == -1) + { goto finished; } - - if (FD_ISSET(sock, &rd)) { + + if (FD_ISSET(sock, &rd)) + { int len = recv(sock, data, 1024, 0); - int i; - struct sap_server_ident *ident; - - for (i = 2; i < len; i += 64) { - ident = (struct sap_server_ident *)(data+i); - if ( (strncmp(name,ident->server_name, - name_len)==0) - && (name_len < 48) - && (ident->server_name[name_len] == '\0')) - { - result->sipx_family = AF_IPX; - result->sipx_network = - ident->server_network; - result->sipx_port = ident->server_port; - ipx_assign_node(result->sipx_node, - ident->server_node); - res = 0; - goto finished; - } + + if (len < 96) + { + continue; } } else { - printf("nobody answered, server %s not found\n", - name); - exit(1); + goto finished; + } + } while (ntohs(*((__u16 *)data)) != IPX_SAP_NEAREST_RESPONSE); + + ident = (struct sap_server_ident *)(data+2); + + /* If the server we got back is the correct one, we normally + would not need to ask for the NET_ADDRESS property. But we + try to connect anyway to check whether there's a valid + route to the server's internal network. Because this one + request is not very expensive, we always do it. */ + + server.addr.sipx_family = AF_IPX; + server.addr.sipx_network = ident->server_network; + server.addr.sipx_port = ident->server_port; + ipx_assign_node(server.addr.sipx_node, ident->server_node); + + if (ncp_connect(&server) != 0) + { + if ( (errno != ENETUNREACH) + || (ipx_make_reachable(ntohl(server.addr.sipx_network))!=0) + || (ncp_connect(&server) != 0)) { + goto finished; } } + if (ncp_read_property_value(&server, NCP_BINDERY_FSERVER, + name, 1, "NET_ADDRESS", + &prop) != 0) + { + ncp_disconnect(&server); + goto finished; + } + + if (ncp_disconnect(&server) != 0) + { + goto finished; + } + + result->sipx_family = AF_IPX; + result->sipx_network = n_addr->network; + result->sipx_port = n_addr->port; + ipx_assign_node(result->sipx_node, n_addr->node); + + res = 0; + finished: close(sock); return res; @@ -455,6 +620,7 @@ main(int argc, char *argv[]) struct stat st; struct ncp_server serv; struct ncp_server *server = &serv; + char mount_name[256]; int fd; int Got_Password; @@ -536,8 +702,12 @@ main(int argc, char *argv[]) addr.sipx_type = NCP_PTYPE; if (bind(ncp_sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { - fprintf(stderr, "bind(ncp_sock, ): %s\n", + fprintf(stderr, "\nbind: %s\n", strerror(errno)); + fprintf(stderr, + "\nMaybe you want to use \n" + "ipx_configure --auto_interface=on --auto_primary=on\n" + "and try again after waiting a minute.\n\n"); exit(1); } @@ -626,23 +796,19 @@ main(int argc, char *argv[]) printf("mount failed\n"); close(wdog_sock); close(ncp_sock); - printf("Maybe you have no route to the internal net " - "of your server.\n"); return -1; } close(ncp_sock); close(wdog_sock); - server->mount_fid = open(mount_point, O_RDONLY, 0); - server->silent = 1; - - if (server->mount_fid == -1) { + if (ncp_connect_mount(server, mount_point) != 0) { fprintf(stderr, "Could not open %s: %s\n", mount_point, strerror(errno)); umount(mount_point); return -1; } + server->silent = 1; if (ncp_login_user(server, data.username, data.password) != 0) { fprintf(stderr, "login failed\n"); @@ -654,7 +820,11 @@ main(int argc, char *argv[]) return -1; } - ment.mnt_fsname = server_name; + strcpy(mount_name, server_name); + strcat(mount_name, "/"); + strcat(mount_name, data.username); + + ment.mnt_fsname = mount_name; ment.mnt_dir = mount_point; ment.mnt_type = "ncpfs"; ment.mnt_opts = "rw"; diff --git a/ncptest.c b/util/ncptest.c similarity index 57% rename from ncptest.c rename to util/ncptest.c index eb26532..c818658 100644 --- a/ncptest.c +++ b/util/ncptest.c @@ -39,7 +39,222 @@ extern pid_t waitpid(pid_t, int *, int); #include "ipxutil.h" static char *progname; -static char *mount_point; + +static void +str_upper(char *name) +{ + while (*name) { + *name = toupper(*name); + name = name + 1; + } +} + +struct sap_query { + unsigned short query_type; /* net order */ + unsigned short server_type; /* net order */ +}; + +struct sap_server_ident { + unsigned short server_type __attribute__ ((packed)); + char server_name[48] __attribute__ ((packed)); + IPXNet server_network __attribute__ ((packed)); + IPXNode server_node __attribute__ ((packed)); + IPXPort server_port __attribute__ ((packed)); + unsigned short intermediate_network __attribute__ ((packed)); +}; + +void +ipx_fprint_node(FILE* file,IPXNode node) +{ + fprintf(file,"%02X%02X%02X%02X%02X%02X", + (unsigned char)node[0], + (unsigned char)node[1], + (unsigned char)node[2], + (unsigned char)node[3], + (unsigned char)node[4], + (unsigned char)node[5] + ); +} + +void +ipx_fprint_network(FILE* file,IPXNet net) +{ + fprintf(file,"%08lX",ntohl(net)); +} + +void +ipx_fprint_port(FILE* file,IPXPort port) +{ + fprintf(file,"%04X",ntohs(port)); +} + +void +ipx_fprint_saddr(FILE* file,struct sockaddr_ipx* sipx) +{ + ipx_fprint_network(file,sipx->sipx_network); + fprintf(file,":"); + ipx_fprint_node(file,sipx->sipx_node); + fprintf(file,":"); + ipx_fprint_port(file,sipx->sipx_port); +} + +void +ipx_print_node(IPXNode node) +{ + ipx_fprint_node(stdout,node); +} + +void +ipx_print_network(IPXNet net) +{ + ipx_fprint_network(stdout,net); +} + +void +ipx_print_port(IPXPort port) +{ + ipx_fprint_port(stdout,port); +} + +void +ipx_print_saddr(struct sockaddr_ipx* sipx) +{ + ipx_fprint_saddr(stdout,sipx); +} + +int +ipx_sap_find_server(char *_name, int server_type, int timeout, + struct sockaddr_ipx *result) +{ + struct sockaddr_ipx ipxs; + char data[1024]; + int sock; + int opt; + int res = -1; + char name[strlen(_name)+1]; + int name_len = strlen(_name); + fd_set rd, wr, ex; + struct timeval tv; + int packets; + + if (name_len > 48) { + return -1; + } + + strcpy(name, _name); + str_upper(name); + + sock=socket(AF_IPX,SOCK_DGRAM,PF_IPX); + if (sock==-1) { + return -1; + } + + opt=1; + /* Permit broadcast output */ + if(setsockopt(sock,SOL_SOCKET,SO_BROADCAST, &opt,sizeof(opt))==-1) + { + perror("setsockopt"); + goto finished; + } + + memset(&ipxs, 0, sizeof(ipxs)); + ipxs.sipx_family=AF_IPX; + ipxs.sipx_network=htonl(0x0); + ipxs.sipx_port=htons(0x0); + ipxs.sipx_type=IPX_SAP_PTYPE; + + if(bind(sock,(struct sockaddr*)&ipxs,sizeof(ipxs))==-1) + { + perror("bind"); + goto finished; + } + + *(unsigned short *)data = htons(0x0001); + *(unsigned short *)&(data[2]) = htons(server_type); + + memset(&ipxs, 0, sizeof(ipxs)); + ipxs.sipx_family=AF_IPX; + ipxs.sipx_network=htonl(0x0); + ipx_assign_node(ipxs.sipx_node, IPX_BROADCAST_NODE); + ipxs.sipx_port=htons(IPX_SAP_PORT); + ipxs.sipx_type=IPX_SAP_PTYPE; + + if (sendto(sock, data, 4, 0, + (struct sockaddr *)&ipxs, sizeof(ipxs)) < 0) { + perror("sendto"); + goto finished; + } + + packets = 10; + while (packets > 0) { + + packets -= 1; + + FD_ZERO(&rd); FD_ZERO(&wr); FD_ZERO(&ex); + FD_SET(sock, &rd); + + tv.tv_sec = timeout; + tv.tv_usec = 0; + + if (select(sock+1, &rd, &wr, &ex, &tv) == -1) { + perror("select"); + goto finished; + } + + if (FD_ISSET(sock, &rd)) { + int len = recv(sock, data, 1024, 0); + int i; + struct sap_server_ident *ident; + + for (i = 2; i < len; i += 64) { + ident = (struct sap_server_ident *)(data+i); + if ( (strncmp(name,ident->server_name, + name_len)==0) + && (name_len < 48) + && (ident->server_name[name_len] == '\0')) + { + result->sipx_family = AF_IPX; + result->sipx_network = + ident->server_network; + result->sipx_port = ident->server_port; + ipx_assign_node(result->sipx_node, + ident->server_node); + res = 0; + goto finished; + } + } + } + else + { + printf("nobody answered, server %s not found\n", + name); + exit(1); + } + } + + finished: + close(sock); + return res; +} + +int +ipx_sscanf_node(char *buf, unsigned char node[6]) +{ + int i; + int n[6]; + + if ((i = sscanf(buf, "%2x%2x%2x%2x%2x%2x", + &(n[0]), &(n[1]), &(n[2]), + &(n[3]), &(n[4]), &(n[5]))) != 6) { + return i; + } + + for (i=0; i<6; i++) { + node[i] = n[i]; + } + return 6; +} + void test_filesearch(struct ncp_server *server) @@ -178,6 +393,43 @@ test_trunc(struct ncp_server *server) return; } +void +test_touch(struct ncp_server *server) +{ + struct nw_info_struct sys; + struct nw_info_struct me; + struct nw_info_struct blub; + int info_mask; + struct nw_modify_dos_info info; + + if (ncp_do_lookup(server, NULL, "SYS", &sys) != 0) { + printf("lookup error\n"); + return; + } + if (ncp_do_lookup(server, &sys, "ME", &me) != 0) { + printf("lookup error\n"); + return; + } + if (ncp_do_lookup(server, &me, "BLUB.TXT", &blub) != 0) { + printf("lookup error\n"); + return; + } + + info_mask = 0; + memset(&info, 0, sizeof(info)); + + info_mask |= DM_MODIFY_DATE; + info_mask |= DM_MODIFY_TIME; + + if (ncp_modify_file_or_subdir_dos_info(server, &blub, info_mask, + &info) != 0) { + printf("modify error\n"); + return; + } + + return; +} + void test_ls(struct ncp_server *server) { @@ -351,57 +603,37 @@ test_print(struct ncp_server *server) int main(int argc, char **argv) { - struct stat st; struct ncp_server serv; struct ncp_server *server = &serv; - progname = argv[0]; - if (geteuid() != 0) { - fprintf(stderr, "%s must be installed suid root\n", progname); - exit(1); - } - - if (argc == 2) { - mount_point = argv[1]; - } else { - fprintf(stderr, "usage: %s mount-point\n", progname); - printf("defaulting to %s mnt\n", progname); - mount_point = "mnt"; + if (argc != 2) { + printf("usage: %s server\n", argv[0]); + exit(1); } + + if (ipx_sap_find_server(argv[1], IPX_SAP_FILE_SERVER, + 3, &(serv.addr)) != 0) { + printf("could not find server %s\n", argv[1]); + exit(1); + } + + if (ncp_connect(server) != 0) { + printf("could not connect\n"); + exit(1); + } + + if (ncp_login_user(server, "me", "ME") != 0) { + printf("login error\n"); + exit(1); + } + + test_touch(server); + + ncp_disconnect(server); - if (stat(mount_point, &st) == -1) { - fprintf(stderr, "could not find mount point %s: %s\n", - mount_point, strerror(errno)); - exit(1); - } - - server->mount_fid = open(mount_point, O_RDONLY, 0); - server->silent = 0; - - if (server->mount_fid == -1) { - fprintf(stderr, "Could not open %s: %s\n", - mount_point, strerror(errno)); - return -1; - } - -#if 0 - for (i=0; i<5; i++) { - struct ncp_volume_info info; - ncp_get_volume_info_with_number(server, i, &info); - printf("vol %d: %s\n", i, info.volume_name); - } - - test_filesearch(server); - test_getfinfo(server); - test_mkdir(server); - test_ls(server); - -#endif - test_trunc(server); - return 0; } diff --git a/util/ncpumount.c b/util/ncpumount.c new file mode 100644 index 0000000..f70040c --- /dev/null +++ b/util/ncpumount.c @@ -0,0 +1,197 @@ +/* + * ncpumount.c + * + * Copyright (C) 1995 by Volker Lendecke + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* #include */ /* generates a warning here */ +extern pid_t waitpid(pid_t, int *, int); +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static char *progname; + +static void +usage(void) +{ + printf("usage: %s mount-point\n", progname); +} + +static int +umount_ok(const char *mount_point) +{ + int fid = open(mount_point, O_RDONLY, 0); + uid_t mount_uid; + + if (fid == -1) { + fprintf(stderr, "Could not open %s: %s\n", + mount_point, strerror(errno)); + return -1; + } + + if (ioctl(fid, NCP_IOC_GETMOUNTUID, &mount_uid) != 0) { + fprintf(stderr, "%s probably not ncp-filesystem\n", + mount_point); + return -1; + } + + if ( (getuid() != 0) + && (mount_uid != getuid())) { + fprintf(stderr, "You are not allowed to umount %s\n", + mount_point); + return -1; + } + + close(fid); + return 0; +} + +/* Make a canonical pathname from PATH. Returns a freshly malloced string. + It is up the *caller* to ensure that the PATH is sensible. i.e. + canonicalize ("/dev/fd0/.") returns "/dev/fd0" even though ``/dev/fd0/.'' + is not a legal pathname for ``/dev/fd0.'' Anything we cannot parse + we return unmodified. */ +char * +canonicalize (const char *path) +{ + char *canonical = malloc (PATH_MAX + 1); + + if (path == NULL) + return NULL; + + if (realpath (path, canonical)) + return canonical; + + strcpy (canonical, path); + return canonical; +} + + +int +main(int argc, char *argv[]) +{ + int fd; + + char* mount_point; + + struct mntent *mnt; + FILE* mtab; + FILE* new_mtab; + + progname = argv[0]; + + if (geteuid() != 0) { + fprintf(stderr, "%s must be installed suid root\n", progname); + exit(1); + } + + if (argc != 2) { + usage(); + exit(1); + } + + mount_point = canonicalize(argv[1]); + + if (umount_ok(mount_point) != 0) { + exit(1); + } + + if (umount(mount_point) != 0) { + fprintf(stderr, "Could not umount %s: %s\n", + mount_point, strerror(errno)); + exit(1); + } + + if ((fd = open(MOUNTED"~", O_RDWR|O_CREAT|O_EXCL, 0600)) == -1) + { + fprintf(stderr, "Can't get "MOUNTED"~ lock file"); + return 1; + } + close(fd); + + if ((mtab = setmntent(MOUNTED, "r")) == NULL) { + fprintf(stderr, "Can't open " MOUNTED ": %s\n", + strerror(errno)); + return 1; + } + +#define MOUNTED_TMP MOUNTED".tmp" + + if ((new_mtab = setmntent(MOUNTED_TMP, "w")) == NULL) { + fprintf(stderr, "Can't open " MOUNTED_TMP ": %s\n", + strerror(errno)); + endmntent(mtab); + return 1; + } + + while ((mnt = getmntent(mtab)) != NULL) { + if (strcmp(mnt->mnt_dir, mount_point) != 0) { + addmntent(new_mtab, mnt); + } + } + + endmntent(mtab); + + if (fchmod (fileno (new_mtab), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) { + fprintf(stderr, "Error changing mode of %s: %s\n", + MOUNTED_TMP, strerror(errno)); + exit(1); + } + + endmntent(new_mtab); + + if (rename(MOUNTED_TMP, MOUNTED) < 0) { + fprintf(stderr, "Cannot rename %s to %s: %s\n", + MOUNTED, MOUNTED_TMP, strerror(errno)); + exit(1); + } + + if (unlink(MOUNTED"~") == -1) + { + fprintf(stderr, "Can't remove "MOUNTED"~"); + return 1; + } + + return 0; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-indent-level: 8 + * c-brace-imaginary-offset: 0 + * c-brace-offset: -8 + * c-argdecl-indent: 8 + * c-label-offset: -8 + * c-continued-statement-offset: 8 + * c-continued-brace-offset: 0 + * End: + */ diff --git a/nwcrypt.c b/util/nwcrypt.c similarity index 100% rename from nwcrypt.c rename to util/nwcrypt.c diff --git a/nwcrypt.h b/util/nwcrypt.h similarity index 100% rename from nwcrypt.h rename to util/nwcrypt.h diff --git a/start_ipx b/util/start_ipx similarity index 100% rename from start_ipx rename to util/start_ipx