1
1
/******************************************************************************
2
(c) 1998-2000 P.J. Caulfield patrick@tykepenguin.cix.co.uk
2
(c) 1998-2002 P.J. Caulfield patrick@tykepenguin.cix.co.uk
4
4
This program is free software; you can redistribute it and/or modify
5
5
it under the terms of the GNU General Public License as published by
6
6
the Free Software Foundation; either version 2 of the License, or
16
16
// Base class for a task within a FAL server process.
17
// All real tasks subclass this but we provide here some basic services such
17
// All real tasks subclass this but we provide here some basic services such
18
18
// as filename conversion and parsing that they all need.
19
19
#include <sys/types.h>
20
20
#include <sys/socket.h>
54
54
// Send and error packet based on the passed error code
55
55
void fal_task::return_error(int code)
58
58
DAPLOG((LOG_ERR, "fal_task error: %s\n", strerror(code)));
60
60
// If the other end went away there's no point in sending the message
88
88
return vms_format=false;
91
/* Add the virtual "root" directory to the filename */
92
void fal_task::add_vroot(char *name)
97
int oldlen = strlen(name);
99
/* Security measure...if the name has ".." in it then blank the whole lot out and
101
well, it stops them escaping the root
103
if (strstr(name, ".."))
106
if (verbose > 3) DAPLOG((LOG_DEBUG, "add_vroot: name is %s, vroot='%s' (len=%d)\n", name, params.vroot, params.vroot_len));
107
memmove(name + params.vroot_len, name, oldlen);
108
memmove(name, params.vroot, params.vroot_len);
110
/* Make sure it's NUL-terminated */
111
name[oldlen+params.vroot_len] = '\0';
113
if (verbose > 3) DAPLOG((LOG_DEBUG, "add_vroot: name is now %s\n", name));
117
/* Remove the virtual "root" directory to the filename */
118
void fal_task::remove_vroot(char *name)
120
if (params.vroot_len)
122
if (verbose > 3) DAPLOG((LOG_INFO, "remove_vroot: name is %s\n", name));
124
memmove(name, name + params.vroot_len-1, strlen(name)+1);
126
if (verbose > 3) DAPLOG((LOG_INFO, "remove_vroot: name is now %s\n", name));
91
131
// Splits a filename up into volume, directory and file parts.
92
132
// The volume and directory are just for display purposes (they get sent back
93
// to the client). file is the (possibly) wildcard filespec to use for
94
// searching for files.
133
// to the client). file is the (possibly) wildcard filespec to use for
134
// searching for files.
96
136
// Unix filenames are just returned as-is.
101
141
void fal_task::split_filespec(char *volume, char *directory, char *file)
143
// If the remote client is RSX then lower-case the filename
144
if (params.remote_os == 4)
146
dap_connection::makelower(file);
103
149
if (is_vms_name(file))
105
151
// This converts the VMS name to a Unix name and back again. This
154
200
// Resolve all relative bits and symbolic links
155
201
realpath(unixname, fullname);
203
// Remove the vroot, but leave a leading slash
204
remove_vroot(fullname);
157
206
// Find the last slash in the name
158
207
lastslash = fullname + strlen(fullname);
159
208
while (*(--lastslash) != '/') ;
162
211
// expect one as does dapfs.
163
212
if (!strchr(lastslash, '.'))
164
213
strcat(fullname, ".");
166
215
// If it's a directory then add .DIR;1
167
216
if (lstat(unixname, &st)==0 && S_ISDIR(st.st_mode))
169
218
// Take care of dots embedded in directory names (/etc/rc.d)
170
219
if (fullname[strlen(fullname)-1] != '.')
171
220
strcat(fullname, ".");
173
222
strcat(fullname, "DIR;1"); // last dot has already been added
175
224
else // else just add a version number unless the file already has one
323
372
// Remove the trailing colon from the volume name
324
373
if (volume[strlen(volume)-1] == ':')
325
374
volume[strlen(volume)-1] = '\0';
327
376
// If the filename has the dummy SYSDISK volume then start from the
328
377
// filesystem root
329
378
if (strcasecmp(volume, sysdisk_name) == 0)
366
415
// Skip root directory specs
367
if (dir[i] == '0' && strncmp(&dir[i], "000000", 6))
416
if (dir[i] == '0' && (strncmp(&dir[i], "000000", 6) == 0))
372
if (dir[i] == '0' && strncmp(&dir[i], "0,0", 3))
421
if (dir[i] == '0' && (strncmp(&dir[i], "0,0", 3) == 0))
429
479
// Most of the subclasses use these routines to send the file attributes in
430
// response to an ACCESS message.
480
// response to an ACCESS message.
431
481
// The option to send the DEV field is really for CREATE because when VMS
432
482
// sees the block device it tries to send the file in block mode and we then
433
483
// end up with an RMS file on Linux and that's not very useful.
480
530
// There's hardly anything in this message but it keeps VMS quiet
481
if (display & dap_access_message::DISPLAY_ALLOC_MASK)
531
if (display & dap_access_message::DISPLAY_ALLOC_MASK)
483
533
dap_alloc_message alloc_msg;
485
535
if (!alloc_msg.write(conn)) return false;
489
539
if (display & dap_access_message::DISPLAY_DATE_MASK)
491
541
dap_date_message date_msg;
493
543
date_msg.set_cdt(st.st_ctime);
494
544
date_msg.set_rdt(st.st_mtime);
495
545
date_msg.set_rvn(1);
496
546
if (!date_msg.write(conn)) return false;
499
549
// Send the protection
500
550
if (display & dap_access_message::DISPLAY_PROT_MASK)
502
552
dap_protect_message prot_msg;
504
554
prot_msg.set_protection(st.st_mode);
505
555
prot_msg.set_owner(st.st_gid, st.st_uid);
506
556
if (!prot_msg.write(conn)) return false;
510
560
if (display & dap_access_message::DISPLAY_NAME_MASK)
512
562
dap_name_message name_msg;
564
if (vms_format || params.remote_os == 4)
516
566
char vmsname[PATH_MAX];
518
568
make_vms_filespec(filename, vmsname, true);
519
569
name_msg.set_namespec(vmsname);
520
570
name_msg.set_nametype(dap_name_message::FILESPEC);
575
625
// Guess file type
576
if (params.auto_type == fal_params::GUESS_TYPE)
626
if (params.auto_type == fal_params::GUESS_TYPE)
577
627
return guess_file_type(blocksize, send_records, name, attrib_msg);
578
if (params.auto_type == fal_params::CHECK_EXT)
628
if (params.auto_type == fal_params::CHECK_EXT)
579
629
return check_file_type(blocksize, send_records, name, attrib_msg);
584
634
// Guess the file type based on the first few bytes and set the attrib
586
bool fal_task::guess_file_type(unsigned int &block_size, bool &send_records,
636
bool fal_task::guess_file_type(unsigned int &block_size, bool &send_records,
587
637
const char *name, dap_attrib_message *attrib_msg)
589
639
char buf[132]; // Arbitrary amounts R us
758
808
strncpy(num, fileptr, numlen);
759
809
num[numlen] = '\0';
761
811
block_size = atoi(num);
764
814
auto_types *new_type = new auto_types(extension, use_records, block_size);
766
DAPLOG((LOG_DEBUG, "Type: %s, %c, %d\n",
816
DAPLOG((LOG_DEBUG, "Type: %s, %c, %d\n",
767
817
extension, use_records?'r':'b', block_size));
855
905
if (stat(metafile, &st) == -1 && errno == ENOENT)
857
mkdir(metafile, 0700);
907
// Make Unix do as it's fucking told.
908
mode_t old_umask = umask(0);
909
mkdir(metafile, 0777);
859
912
strcat(metafile, "/");
860
913
strcat(metafile, endpath);
863
916
// Read file metadata - return true if it exists, false otherwise
864
bool fal_task::read_metafile(unsigned int &block_size, bool &send_records,
917
bool fal_task::read_metafile(unsigned int &block_size, bool &send_records,
865
918
const char *name, dap_attrib_message *attrib_msg)
867
920
char metafile[PATH_MAX];
917
970
// For variable-length records we always send as records if we
918
971
// need to consult the metadata.
919
if (metadata.rfm & dap_attrib_message::FB$VAR &&
972
if (metadata.rfm & dap_attrib_message::FB$VAR &&
920
973
record_lengths != NULL)
922
975
if (verbose>1) DAPLOG((LOG_DEBUG, "Sending VAR file as records\n"));
956
1009
metadata.mrs = attrib_msg->get_mrs();
957
1010
metadata.version = metafile_data::METAFILE_VERSION;
958
1011
metadata.num_records = current_record;
960
1013
// Calculate Longest Record Length.
961
1014
unsigned int lrl = 0;
962
1015
for (unsigned int i=0; i<current_record; i++)
963
1016
if (record_lengths[i] > lrl) lrl = record_lengths[i];
965
// Our record lengths include the terminating LF so
1018
// Our record lengths include the terminating LF so
966
1019
// subtract that from the length.
967
1020
// If there are no records then put MRS in there.
968
1021
metadata.lrl = (lrl?(lrl - 1):metadata.mrs);
979
1032
if (verbose > 1) DAPLOG((LOG_INFO, "Writing metafile with %d records\n", current_record));
980
1033
::fwrite(record_lengths, sizeof(short), current_record, mf);
1036
// Set the file ownership & protecttion of the metafile so it
1037
// matches the real file.
1039
if (stat(name, &st) == 0)
1041
fchown(fileno(mf), st.st_uid, st.st_gid);
1042
fchmod(fileno(mf), st.st_mode & 0777);
986
1048
// Read VMS NFS "ADF" file - return true if it exists, false otherwise
987
bool fal_task::read_adf(unsigned int &block_size, bool &send_records,
1049
bool fal_task::read_adf(unsigned int &block_size, bool &send_records,
988
1050
const char *name, dap_attrib_message *attrib_msg)
990
1052
char adfname[PATH_MAX];