Add options to give somewhat better compatibility with the AFP server in OS X 10.5.

*The OS X server doesn't support ProDOS-type file type info, so trying to set it will give an error. This normally causes GS/OS to give an error when creating a file. There is now an option to ignore the error and proceed with creating the file, although the file type still won't be set.

*The OS X server seems to restrict each write operation to a small size based on the ASP quantum, so there is now an option for this. Using this option means we may write less than requested, but the AppleShare FST can detect this and proceed to write the rest.
This commit is contained in:
Stephen Heumann 2017-04-22 18:46:26 -05:00
parent 898c4e531c
commit c4286a024e
5 changed files with 127 additions and 26 deletions

View File

@ -46,11 +46,13 @@
#define saveFilePrompt 100 #define saveFilePrompt 100
#define optionsMenu 300 #define optionsMenu 300
#define afpOverTCPOptionsItem 301 #define afpOverTCPOptionsItem 301
#define useLargeReadsItem 302 #define useLargeReadsItem 302
#define forceAFP22Item 303 #define forceAFP22Item 303
#define fakeSleepItem 304 #define fakeSleepItem 304
#define useLargeWritesItem 305
#define ignoreErrorsSettingFileTypesItem 306
#define fstMissingError 3000 #define fstMissingError 3000
#define noEasyMountError 3001 #define noEasyMountError 3001
@ -112,7 +114,8 @@ Word modifiers = 0;
char zoneBuf[ZONE_MAX + 1]; char zoneBuf[ZONE_MAX + 1];
char AFPOverTCPZone[] = "AFP over TCP"; char AFPOverTCPZone[] = "AFP over TCP";
unsigned int flags = fLargeReads; /* for AFP over TCP connections */ /* Default flags for AFP over TCP connections */
unsigned int flags = fLargeReads | fLargeWrites;
void fillEasyMountRec(char *server, char *zone, char *volume, char *user, void fillEasyMountRec(char *server, char *zone, char *volume, char *user,
char *password, char *volpass) char *password, char *volpass)
@ -510,6 +513,14 @@ void DoHit(long ctlID, CtlRecHndl ctlHandle)
} else if (menuItem == fakeSleepItem) { } else if (menuItem == fakeSleepItem) {
flags ^= fFakeSleep; flags ^= fFakeSleep;
CheckMItem((flags & fFakeSleep) ? TRUE : FALSE, fakeSleepItem); CheckMItem((flags & fFakeSleep) ? TRUE : FALSE, fakeSleepItem);
} else if (menuItem == useLargeWritesItem) {
flags ^= fLargeWrites;
CheckMItem((flags & fLargeWrites) ? TRUE : FALSE,
useLargeWritesItem);
} else if (menuItem == ignoreErrorsSettingFileTypesItem) {
flags ^= fIgnoreFileTypeErrors;
CheckMItem((flags & fIgnoreFileTypeErrors) ? TRUE : FALSE,
ignoreErrorsSettingFileTypesItem);
} }
SetCtlValue(afpOverTCPOptionsItem, ctlHandle); SetCtlValue(afpOverTCPOptionsItem, ctlHandle);

View File

@ -93,11 +93,13 @@ resource rIcon (1) {
#define saveFilePrompt 100 #define saveFilePrompt 100
#define optionsMenu 300 #define optionsMenu 300
#define afpOverTCPOptionsItem 301 #define afpOverTCPOptionsItem 301
#define useLargeReadsItem 302 #define useLargeReadsItem 302
#define forceAFP22Item 303 #define forceAFP22Item 303
#define fakeSleepItem 304 #define fakeSleepItem 304
#define useLargeWritesItem 305
#define ignoreErrorsSettingFileTypesItem 306
/* /*
* Controls in the control panel window (for 640 mode or 320 mode) * Controls in the control panel window (for 640 mode or 320 mode)
@ -245,7 +247,14 @@ resource rMenu (optionsMenu) {
optionsMenu, /* menu ID */ optionsMenu, /* menu ID */
refIsResource*menuTitleRefShift + refIsResource*itemRefShift, refIsResource*menuTitleRefShift + refIsResource*itemRefShift,
optionsMenu, /* menu title ref (not drawn) */ optionsMenu, /* menu title ref (not drawn) */
{afpOverTCPOptionsItem, useLargeReadsItem, forceAFP22Item, fakeSleepItem}; {
afpOverTCPOptionsItem,
useLargeReadsItem,
useLargeWritesItem,
forceAFP22Item,
fakeSleepItem,
ignoreErrorsSettingFileTypesItem
};
}; };
resource rPString(optionsMenu,noCrossBank) { "" }; resource rPString(optionsMenu,noCrossBank) { "" };
@ -266,7 +275,16 @@ resource rMenuItem (useLargeReadsItem) {
useLargeReadsItem /* menu item title ref */ useLargeReadsItem /* menu item title ref */
}; };
resource rPString(useLargeReadsItem,noCrossBank) { "Use Large Reads" }; resource rPString(useLargeReadsItem,noCrossBank) { "Use Large Reads" };
resource rMenuItem (useLargeWritesItem) {
useLargeWritesItem, /* menu item ID */
"","",
$12,
refIsResource*itemTitleRefShift,
useLargeWritesItem /* menu item title ref */
};
resource rPString(useLargeWritesItem,noCrossBank) { "Use Large Writes" };
resource rMenuItem (forceAFP22Item) { resource rMenuItem (forceAFP22Item) {
forceAFP22Item, /* menu item ID */ forceAFP22Item, /* menu item ID */
"","", "","",
@ -285,6 +303,17 @@ resource rMenuItem (fakeSleepItem) {
}; };
resource rPString(fakeSleepItem,noCrossBank) { "Fake Sleep to Keep Alive" }; resource rPString(fakeSleepItem,noCrossBank) { "Fake Sleep to Keep Alive" };
resource rMenuItem (ignoreErrorsSettingFileTypesItem) {
ignoreErrorsSettingFileTypesItem, /* menu item ID */
"","",
0,
refIsResource*itemTitleRefShift,
ignoreErrorsSettingFileTypesItem /* menu item title ref */
};
resource rPString(ignoreErrorsSettingFileTypesItem,noCrossBank) {
"Ignore Errors Setting File Types"
};
/* /*
* Controls in the help window * Controls in the help window
*/ */

View File

@ -10,7 +10,9 @@
AFPOptions afpOptions[] = AFPOptions afpOptions[] =
{ {
{"LR", fLargeReads}, {"LR", fLargeReads},
{"LW", fLargeWrites},
{"22", fForceAFP22}, {"22", fForceAFP22},
{"FS", fFakeSleep}, {"FS", fFakeSleep},
{"IE", fIgnoreFileTypeErrors},
{0, 0} {0, 0}
}; };

View File

@ -1,9 +1,11 @@
#ifndef AFPOPTIONS_H #ifndef AFPOPTIONS_H
#define AFPOPTIONS_H #define AFPOPTIONS_H
#define fLargeReads 0x0001 #define fLargeReads 0x0001
#define fForceAFP22 0x0002 #define fForceAFP22 0x0002
#define fFakeSleep 0x0004 #define fFakeSleep 0x0004
#define fIgnoreFileTypeErrors 0x0008
#define fLargeWrites 0x0010
typedef struct AFPOptions { typedef struct AFPOptions {
char *optString; char *optString;

View File

@ -26,9 +26,32 @@ typedef struct FPReadRec {
Byte NewLineChar; Byte NewLineChar;
} FPReadRec; } FPReadRec;
typedef struct FPWriteRec {
Word CommandCode; /* includes pad byte */
Word OForkRecNum;
LongWord Offset;
LongWord ReqCount;
} FPWriteRec;
typedef struct FPSetFileDirParmsRec {
Word CommandCode; /* includes pad byte */
Word VolumeID;
LongWord DirectoryID;
Word Bitmap;
Byte PathType;
/* Pathname and parameters follow */
} FPSetFileDirParmsRec;
#define kFPRead 27 #define kFPRead 27
#define kFPLogin 18 #define kFPLogin 18
#define kFPZzzzz 122 #define kFPZzzzz 122
#define kFPSetFileDirParms 35
#define kFPBitmapErr (-5004L)
#define kFPProDOSInfoBit 0x2000 /* In file/directory bitmaps */
#define ASPQuantumSize 4624
/* For forced AFP 2.2 login */ /* For forced AFP 2.2 login */
static Byte loginBuf[100]; static Byte loginBuf[100];
@ -196,13 +219,13 @@ ret:
* after every command we send. This avoids having the server * after every command we send. This avoids having the server
* disconnect us because we can't send tickles for a while. * disconnect us because we can't send tickles for a while.
* *
* This implementation is designed to work with Netatalk: * This implementation differs from Apple's specifications in a couple
* -Apple's docs say FPZzzzz was introduced with AFP 2.3, but Netatalk * respects, but matches the actual behavior of the servers I've tried:
* supports it with AFP 2.2 and doesn't support AFP 2.3 at all. *
* -Apple's docs also say there is no reply to FPZzzzz, but Netatalk * -Apple's docs say FPZzzzz was introduced with AFP 2.3, but Netatalk and
* send a reply with four bytes of data. * OS X 10.5 support it with AFP 2.2 and doesn't support AFP 2.3 at all.
* Netatalk includes comments indicating Apple's implementation in OS X * -Apple's docs show no reply block for FPZzzzz, but Netatalk and
* may really work more like Netatalk, but I haven't checked this. * OS X 10.5 send a reply with four bytes of data.
*/ */
if ((sess->atipMapping.flags & fFakeSleep) if ((sess->atipMapping.flags & fFakeSleep)
&& sess->loggedIn && commandRec->result == 0 && sess->loggedIn && commandRec->result == 0
@ -355,12 +378,27 @@ static void DoSPCommand(Session *sess, ASPCommandRec *commandRec) {
} }
static void DoSPWrite(Session *sess, ASPWriteRec *commandRec) { static void DoSPWrite(Session *sess, ASPWriteRec *commandRec) {
LongWord dataLength;
sess->request.flags = DSI_REQUEST; sess->request.flags = DSI_REQUEST;
sess->request.command = DSIWrite; sess->request.command = DSIWrite;
sess->request.requestID = htons(sess->nextRequestID++); sess->request.requestID = htons(sess->nextRequestID++);
sess->request.writeOffset = htonl(commandRec->cmdBlkLength); sess->request.writeOffset = htonl(commandRec->cmdBlkLength);
sess->request.totalDataLength = dataLength = commandRec->writeDataLength;
htonl(commandRec->cmdBlkLength + commandRec->writeDataLength);
/*
* If requested, limit the data size of a single DSIWrite command to 4623.
* This is necessary to make writes work on OS X (and others?).
*/
if (!(sess->atipMapping.flags & fLargeWrites)
&& dataLength > ASPQuantumSize - 1)
{
dataLength = ASPQuantumSize - 1;
((FPReadRec*)commandRec->cmdBlkAddr)->ReqCount = htonl(dataLength);
}
sess->request.totalDataLength =
htonl(dataLength + commandRec->cmdBlkLength);
sess->replyBuf = (void*)(commandRec->replyBufferAddr & 0x00FFFFFF); sess->replyBuf = (void*)(commandRec->replyBufferAddr & 0x00FFFFFF);
sess->replyBufLen = commandRec->replyBufferLen; sess->replyBufLen = commandRec->replyBufferLen;
@ -411,13 +449,32 @@ void FinishASPCommand(Session *sess) {
((ASPCommandRec*)(sess->spCommandRec))->cmdResult = ((ASPCommandRec*)(sess->spCommandRec))->cmdResult =
sess->reply.errorCode; sess->reply.errorCode;
((ASPCommandRec*)(sess->spCommandRec))->replyLength = dataLength; ((ASPCommandRec*)(sess->spCommandRec))->replyLength = dataLength;
/*
* If requested by user, ignore errors when setting ProDOS-style
* filetype info. This allows files to be written to servers running
* old versions of OS X, although filetypes are not set correctly.
*/
if ((sess->atipMapping.flags & fIgnoreFileTypeErrors)
&& ((ASPCommandRec*)(sess->spCommandRec))->cmdBlkLength
> sizeof(FPSetFileDirParmsRec)
&& *(Byte*)(((ASPCommandRec*)(sess->spCommandRec))->cmdBlkAddr)
== kFPSetFileDirParms
&& (((FPSetFileDirParmsRec*)(((ASPCommandRec*)(sess->spCommandRec))
->cmdBlkAddr))->Bitmap & htons(kFPProDOSInfoBit))
&& sess->reply.errorCode == htonl((LongWord)kFPBitmapErr))
{
((ASPCommandRec*)(sess->spCommandRec))->cmdResult = 0;
}
break; break;
case aspWriteCommand: case aspWriteCommand:
((ASPWriteRec*)(sess->spCommandRec))->cmdResult = ((ASPWriteRec*)(sess->spCommandRec))->cmdResult =
sess->reply.errorCode; sess->reply.errorCode;
((ASPWriteRec*)(sess->spCommandRec))->replyLength = dataLength; ((ASPWriteRec*)(sess->spCommandRec))->replyLength = dataLength;
((ASPWriteRec*)(sess->spCommandRec))->writtenLength = ((ASPWriteRec*)(sess->spCommandRec))->writtenLength =
((ASPWriteRec*)(sess->spCommandRec))->writeDataLength; ntohl(sess->request.totalDataLength)
- ((ASPWriteRec*)(sess->spCommandRec))->cmdBlkLength;
break; break;
} }