Exemple #1
0
        public VersionYesNoBlock(CopyDiskBlock command, ushort code, string herald)
        {
            Code   = code;
            Herald = new BCPLString(herald);

            Length  = (ushort)((6 + herald.Length + 2) / 2);    // +2 for length of BCPL string and to round up to next word length
            Command = (ushort)command;
        }
Exemple #2
0
        private void CopyDiskWorkerThread()
        {
            // TODO: enforce state (i.e. reject out-of-order block types.)
            while (_running)
            {
                // Retrieve length of this block (in bytes):
                int length = Channel.ReadUShort() * 2;

                // Sanity check that length is a reasonable value.
                if (length > 2048)
                {
                    Channel.SendAbort("Block length is invalid.");
                    throw new InvalidOperationException(String.Format("Insane block length ({0})", length));
                }

                // Retrieve type
                CopyDiskBlock blockType = (CopyDiskBlock)Channel.ReadUShort();

                // Read rest of block starting at offset 4 (so deserialization works)
                byte[] data = new byte[length];
                Channel.Read(ref data, data.Length - 4, 4);

                Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Copydisk block type is {0}", blockType);

                switch (blockType)
                {
                case CopyDiskBlock.Version:
                {
                    VersionYesNoBlock vbIn = (VersionYesNoBlock)Serializer.Deserialize(data, typeof(VersionYesNoBlock));

                    Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Copydisk client is version {0}, '{1}'", vbIn.Code, vbIn.Herald.ToString());

                    // Send the response:
                    VersionYesNoBlock vbOut = new VersionYesNoBlock(CopyDiskBlock.Version, vbIn.Code, "LCM+L IFS CopyDisk of 26-Jan-2016");
                    Channel.Send(Serializer.Serialize(vbOut));
                }
                break;

                case CopyDiskBlock.Login:
                {
                    LoginBlock login = (LoginBlock)Serializer.Deserialize(data, typeof(LoginBlock));

                    Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Login is for user '{0}', password '{1}', connection '{2}', connection password '{3}'.",
                              login.UserName,
                              login.UserPassword,
                              login.ConnName,
                              login.ConnPassword);

                    _userToken = AuthenticateUser(login.UserName.ToString(), login.UserPassword.ToString());

                    if (_userToken != null)
                    {
                        //
                        // Send a "Yes" response back.
                        //
                        VersionYesNoBlock yes = new VersionYesNoBlock(CopyDiskBlock.Yes, 0, "Come on in, the water's fine.");
                        Channel.Send(Serializer.Serialize(yes));
                    }
                    else
                    {
                        //
                        // Send a "No" response back indicating the login failure.
                        //
                        VersionYesNoBlock no = new VersionYesNoBlock(CopyDiskBlock.No, (ushort)NoCode.IllegalOrIncorrectPassword, "Invalid username or password.");
                        Channel.Send(Serializer.Serialize(no), true);
                    }
                }
                break;

                case CopyDiskBlock.SendDiskParamsR:
                {
                    SendDiskParamsBlock p = (SendDiskParamsBlock)Serializer.Deserialize(data, typeof(SendDiskParamsBlock));

                    Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Requested unit for reading is '{0}'", p.UnitName);

                    //
                    // See if the pack image exists, return HereAreDiskParams if so, or No if not.
                    // If the image exists, save the path for future use.
                    //
                    // Some sanity (and security) checks:
                    // Name must be a filename only, no paths of any kind allowed.
                    // Oh, and the file must exist in the directory holding disk packs.
                    //
                    string diskPath = GetPathForDiskImage(p.UnitName.ToString());
                    if (!String.IsNullOrEmpty(Path.GetDirectoryName(p.UnitName.ToString())) ||
                        !File.Exists(diskPath))
                    {
                        // Invalid name, return No reponse.
                        VersionYesNoBlock no = new VersionYesNoBlock(CopyDiskBlock.No, (ushort)NoCode.UnitNotReady, "Invalid unit name.");
                        Channel.Send(Serializer.Serialize(no));
                    }
                    else
                    {
                        //
                        // Attempt to open the image file and read it into memory.
                        //
                        try
                        {
                            using (FileStream packStream = new FileStream(diskPath, FileMode.Open, FileAccess.Read))
                            {
                                // TODO: determine pack type rather than assuming Diablo 31
                                _pack = new DiabloPack(DiabloDiskType.Diablo31);
                                _pack.Load(packStream, diskPath, true /* reverse byte order */);
                            }

                            // Send a "HereAreDiskParams" response indicating success.
                            //
                            HereAreDiskParamsBFSBlock diskParams = new HereAreDiskParamsBFSBlock(_pack.Geometry);
                            Channel.Send(Serializer.Serialize(diskParams));
                        }
                        catch
                        {
                            // If we fail for any reason, return a "No" response.
                            // TODO: can we be more helpful here?
                            VersionYesNoBlock no = new VersionYesNoBlock(CopyDiskBlock.No, (ushort)NoCode.UnitNotReady, "Image could not be opened.");
                            Channel.Send(Serializer.Serialize(no));
                        }
                    }
                }
                break;

                case CopyDiskBlock.SendDiskParamsW:
                {
                    SendDiskParamsBlock p = (SendDiskParamsBlock)Serializer.Deserialize(data, typeof(SendDiskParamsBlock));

                    Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Requested unit for writing is '{0}'", p.UnitName);

                    //
                    // Some sanity (and security) checks:
                    // Name must be a filename only, no paths of any kind allowed.
                    // Oh, and the file must not exist in the directory holding disk packs.
                    //
                    string diskPath = GetPathForDiskImage(p.UnitName.ToString());
                    if (!String.IsNullOrEmpty(Path.GetDirectoryName(p.UnitName.ToString())) ||
                        File.Exists(diskPath))
                    {
                        // Invalid name, return No reponse.
                        VersionYesNoBlock no = new VersionYesNoBlock(CopyDiskBlock.No, (ushort)NoCode.UnitNotReady, "Invalid unit name or image already exists.");
                        Channel.Send(Serializer.Serialize(no));
                    }
                    else
                    {
                        //
                        // Create a new in-memory disk image.  We will write it out to disk when the transfer is completed.
                        //
                        // TODO: determine pack type based on disk params rather than assuming Diablo 31
                        _pack          = new DiabloPack(DiabloDiskType.Diablo31);
                        _pack.PackName = diskPath;


                        // Send a "HereAreDiskParams" response indicating success.
                        //
                        HereAreDiskParamsBFSBlock diskParams = new HereAreDiskParamsBFSBlock(_pack.Geometry);
                        Channel.Send(Serializer.Serialize(diskParams));
                    }
                }
                break;

                case CopyDiskBlock.HereAreDiskParams:
                {
                    HereAreDiskParamsBFSBlock diskParams = (HereAreDiskParamsBFSBlock)Serializer.Deserialize(data, typeof(HereAreDiskParamsBFSBlock));

                    Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Disk params are: Type {0}, C/H/S {1}/{2}/{3}",
                              diskParams.DiskType,
                              diskParams.Cylinders,
                              diskParams.Heads,
                              diskParams.Sectors);
                }
                break;

                case CopyDiskBlock.RetrieveDisk:
                case CopyDiskBlock.StoreDisk:
                {
                    TransferParametersBlock transferParameters = (TransferParametersBlock)Serializer.Deserialize(data, typeof(TransferParametersBlock));

                    _startAddress = _pack.DiskAddressToVirtualAddress(transferParameters.StartAddress);
                    _endAddress   = _pack.DiskAddressToVirtualAddress(transferParameters.EndAddress);

                    // Validate that the user is allowed to store.
                    if (blockType == CopyDiskBlock.StoreDisk)
                    {
                        if (_userToken.Privileges != IFSPrivileges.ReadWrite)
                        {
                            VersionYesNoBlock no = new VersionYesNoBlock(CopyDiskBlock.No, (ushort)NoCode.UnitWriteProtected, "You do not have permission to store disk images.");
                            Channel.Send(Serializer.Serialize(no));
                            break;
                        }
                    }

                    Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Transfer is from block {0} to block {1}", transferParameters.StartAddress, transferParameters.EndAddress);

                    // Validate start/end parameters
                    if (_endAddress <= _startAddress ||
                        _startAddress > _pack.MaxAddress ||
                        _endAddress > _pack.MaxAddress)
                    {
                        VersionYesNoBlock no = new VersionYesNoBlock(CopyDiskBlock.No, (ushort)NoCode.UnknownCommand, "Transfer parameters are invalid.");
                        Channel.Send(Serializer.Serialize(no));
                    }
                    else
                    {
                        // We're OK.  Save the parameters and send a Yes response.
                        VersionYesNoBlock yes = new VersionYesNoBlock(CopyDiskBlock.Yes, 0, "You are cleared for launch.");
                        Channel.Send(Serializer.Serialize(yes));

                        //
                        // And send the requested range of pages if this is a Retrieve operation
                        // (otherwise wait for a HereIsDiskPage block from the client.)
                        //
                        if (blockType == CopyDiskBlock.RetrieveDisk)
                        {
                            for (int i = _startAddress; i < _endAddress + 1; i++)
                            {
                                DiabloDiskSector    sector = _pack.GetSector(i);
                                HereIsDiskPageBlock block  = new HereIsDiskPageBlock(sector.Header, sector.Label, sector.Data);
                                Channel.Send(Serializer.Serialize(block), false /* do not flush */);

                                if ((i % 100) == 0)
                                {
                                    Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Sent page {0}", i);
                                }
                            }

                            // Send "EndOfTransfer" block to finish the transfer.
                            EndOfTransferBlock endTransfer = new EndOfTransferBlock(0);
                            Channel.Send(Serializer.Serialize(endTransfer));

                            Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Send done.");
                        }
                        else
                        {
                            _currentAddress = _startAddress;
                        }
                    }
                }
                break;

                case CopyDiskBlock.HereIsDiskPage:
                {
                    if (_currentAddress > _endAddress)
                    {
                        Channel.SendAbort("Invalid address for page.");
                        _running = false;
                        break;
                    }

                    if ((_currentAddress % 100) == 0)
                    {
                        Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Received page {0}", _currentAddress);
                    }

                    if (data.Length < 512 + 16 + 4 + 4)
                    {
                        // Incomplete ("incorrigable") page, indicating an unreadable or empty sector, just copy
                        // the header/label data in and leave an empty data page.
                        HereIsDiskPageIncorrigableBlock diskPage = (HereIsDiskPageIncorrigableBlock)Serializer.Deserialize(data, typeof(HereIsDiskPageIncorrigableBlock));
                        DiabloDiskSector sector = new DiabloDiskSector(diskPage.Header, diskPage.Label, new byte[512]);
                        _pack.SetSector(_currentAddress, sector);

                        _currentAddress++;

                        Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Page is empty / incorrigable.");
                    }
                    else
                    {
                        HereIsDiskPageBlock diskPage = (HereIsDiskPageBlock)Serializer.Deserialize(data, typeof(HereIsDiskPageBlock));
                        DiabloDiskSector    sector   = new DiabloDiskSector(diskPage.Header, diskPage.Label, diskPage.Data);
                        _pack.SetSector(_currentAddress, sector);

                        _currentAddress++;
                    }
                }
                break;

                case CopyDiskBlock.EndOfTransfer:
                {
                    // No data in block.  If we aren't currently at the end of the transfer, the transfer has been aborted.
                    // Do nothing right now.
                    if (_currentAddress < _endAddress)
                    {
                        Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Transfer was aborted.");
                        _running = false;
                    }
                    else
                    {
                        try
                        {
                            // Commit disk image to disk.
                            using (FileStream packStream = new FileStream(_pack.PackName, FileMode.OpenOrCreate, FileAccess.Write))
                            {
                                Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Saving {0}...", _pack.PackName);
                                _pack.Save(packStream, true /* reverse byte order */);
                                Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Saved.");
                            }
                        }
                        catch (Exception e)
                        {
                            // Log error, reset state.
                            Log.Write(LogType.Error, LogComponent.CopyDisk, "Failed to save pack {0} - {1}", _pack.PackName, e.Message);
                        }
                    }
                }
                break;

                case CopyDiskBlock.SendErrors:
                {
                    Log.Write(LogType.Verbose, LogComponent.CopyDisk, "Sending error summary...");
                    // No data in block.  Send list of errors we encountered.  (There should always be none since we're perfect and have no disk errors.)
                    HereAreErrorsBFSBlock errorBlock = new HereAreErrorsBFSBlock(0, 0);
                    Channel.Send(Serializer.Serialize(errorBlock));
                }
                break;

                default:
                    Log.Write(LogType.Warning, LogComponent.CopyDisk, "Unhandled CopyDisk block {0}", blockType);
                    break;
                }
            }

            if (OnExit != null)
            {
                OnExit(this);
            }
        }