メインコンテンツへスキップ

SFTPはSSHの一部であるFTPサブシステムであり、暗号化されたファイル転送を可能にします。 先週、SFTPによるファイル転送が正常に行われない問題が2件発生しました。1件はOpenVOSがファイル送信元、もう1件はOpenVOSがファイル受信側でした。いずれのケースでもファイルはASCIIデータを含んでおり、問題はWindows テキストファイルの行末処理の違いに起因していました。

OpenVOSをソースとして始めましょう。お気に入りのエディタでテキストファイルを作成すると、順次ファイルが生成されます。例えば

d test
%phx_vos#m16_mas>SysAdmin>Noah_Davids>test  10-09-05 08:13:49 mst
12345
67890

ready 08:13:49

図1 – テキストファイルの表示
display_file_status コマンドは、これがシーケンシャルファイルであり、データバイト数が 10 であるにもかかわらず、実際にはファイル内に 20 バイトが存在することを示しています。
display_file_status test
name: %phx_vos#m16_mas>SysAdmin>Noah_Davids>test
file organization: sequential file
. . .
next byte:                 20
blocks used: 1
. . .
record count: 2
data byte count:           10

ready 08:14:01

図2 – display_file の状態
dump_file コマンドはファイルの実際の構造を表示します。各「行」の前後には、その長さを示す 2 バイトが配置されています。これらの長さバイトは必ず偶数バイト境界から始まる必要があるため、埋め込みバイトも存在します。特に注目すべき点は、行末文字(改行コード 0x0A または 改行コード 0x0D0A)が一切存在しないことです。
dump_file test%phx_vos#m16_mas>システム管理者>ノア・デイヴィッズ>テスト 10-09-05 08:14:07 mst
ブロック番号 1000  00053132 333435FF 00050005 36373839 |..12345…..6789|010 30FF0005 FFFFFFFF FFFFFFFF FFFFFFFF |0……………|020 FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |…………….|

=

FF0 FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |…………….|

準備完了 08:14:07

図3 – ファイルの実際の構造を示すdump_file

 

SFTPを使用してファイルを転送すると、OpenVOSは行から長さバイトを削除し、改行文字を追加します。その結果、元のファイルが10種類のデータタイプを含んでいたのに対し、転送されたファイルには12種類が含まれることになります。
C:Documents and SettingsnoahMy Documentstemp>"C:Program FilesPuTTYpsftp" [email protected]
Using username "nd".
[email protected]'s password:
Remote working directory is /SysAdmin/Noah_Davids
psftp> get test test.txt
remote:/SysAdmin/Noah_Davids/test => local:test.txt
psftp> quit
C:Documents and SettingsnoahMy Documentstemp>dir
Volume in drive C has no label.
Volume Serial Number is 38B1-9C13
Directory of C:Documents and SettingsnoahMy Documentstempblog - sftp
08/27/2010 01:50 PM <DIR> .
08/27/2010 01:50 PM <DIR> ..
08/27/2010 01:50 PM 12 test.txt
1 File(s) 12 bytes
2 Dir(s) 39,471,644,672 bytes free
図4 – 転送されたファイルの長さが元のファイルの長さと一致しない
さらに、メモ帳のようなものでファイルを表示すると、行末に奇妙な記号が表示され、1行として表示されます。
図5 – メモ帳でのファイル表示
ファイルの16進ダンプを取得するユーティリティを使用すると、各行の末尾に改行コード(0x0A)が存在することが確認できます。標準的なWindows 終端シーケンスはキャリッジリターン・改行(0x0D0A)です。メモ帳は改行コードのみを解釈する方法を知りませんが、他のエディタではファイルを正しく表示します。

 

図6 – 転送されたファイルの16進ダンプ
その結果、転送されたファイルは破損しているように見え、長さが異なり、少なくともメモ帳では表示できません。転送前にファイルをストリームファイルに変換すると、長さの問題は修正されます。両方のファイルは同じ長さを示すようになります。ただし、改行コードの問題は残ります。

 

では、OpenVOSWindows からファイルを受信する場合はどうなるでしょうか? 既に述べたように、MicrosoftWindows 行末を改行コード(CRLF)Windows 。
図7 – MicrosoftWindows 上で作成されたファイルの16進ダンプ
OpenVOS SFTPサブシステムはストリームファイルを作成します。改行コードは行終端文字として扱われ、キャリッジリターンコードは行データの一部として扱われます。

dump_file pc1.txt

%phx_vos#m16_mas>SysAdmin>Noah_Davids>pc1.txt 10-09-05 08:52:21 mst
ブロック番号 1

000 61626364650D0A666768696A0D0AFFFF |abcde..fghij….|

010 FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |…………….|

=

FF0 FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |…………….|

準備完了 08:52:21

図8 – 転送されたファイルのダンプファイル
displayコマンドはファイルを正しく表示します。

d pc1.txt

%phx_vos#m16_mas>システム管理者>ノア・デイヴィッズ>pc1.txt 10-09-05 08:54:25 mst

abcde

fghij

準備完了 08:54:25

図9 – 転送されたファイルの表示
しかし実際にファイルを読み込むと、改行コードが各行の末尾に表示されます。
test_system_calls
tsc: s$attach_port p pc1.txt
tsc: s$open
tsc: s$seq_read p
Buffer length = 6
00000000 61626364 650D |abcde. |
tsc: s$seq_read p
Buffer length = 6
00000000 66676869 6A0D |fghij. |

tsc:

図10 – ファイルを読み込むとキャリッジリターン文字が表示される
結果として、ファイルが破損しているように見える。
以下のPerlスクリプトは、キャリッジリターン文字の追加または削除に使用できます。MicrosoftWindows にファイルを転送する前に追加し、MicrosoftWindows からファイルを転送した後に削除することを推奨します。
# cr.pl begins here
#
# cr
# version 1.0 10-08-27
#
use strict;
use warnings;
use Getopt::Long;
my ($inFile, $outFile, @files, $add, $remove);
my ($INFILE);
my ($result, $count, $verbose, $addremove);
$result = GetOptions ('in=s' => $inFile,
'out=s' => $outFile,
'add' => $add,
'remove' => $remove,
'verbose=s' => $verbose);
if (($result != 1) || !defined ($inFile) || !defined ($outFile))
{
print "nnUsage:n";
print "perl cr.pl -in PATH -out PATH [[-add] | [-remove]] [-verbose]}n";
exit;
}
if (defined ($add) && defined ($remove))
{
print "You can only specify -add or -remove not bothnn";
print "nnUsage:n";
print "perl cr.pl -in PATH -out PATH [[-add] | [-remove]] [-verbose]}n";
exit;
}
@files = glob ($inFile);
if (@files < 1) {print "nNo files found for " . $inFile . "nn";}
if (@files > 1) {print "nMore than 1 file found for " . $inFile . "nn";}
open (OUT, ">".$outFile) || die "Can't open output file " . $outFile . "nn";
open ($INFILE, $files[0]) || die "Can't open input file " . $files[0] . "nn";
if (!defined ($verbose)) { $verbose = -1; }
$count = 0;
while ($_ = <$INFILE>)
{
if (defined ($remove))
{
s/r//;
print OUT $_ ;
}
else
{
s/n//;
print OUT $_ . "rn";
}
$count++;
if (($verbose > 0) && ($count % $verbose) == 0)
{ print "Line " . $count . " of " . $files[0] . " processedn"; }
}
close $INFILE;
#

# cr ends here

図11 – キャリッジリターン文字の削除または追加を行うPerlスクリプト
FTPはなぜ動作しているように見えるのか?FTPにはASCIIモードとバイナリモードの2つの転送モードがある。ASCIIモードでWindows OpenVOSへ転送すると、レコードからキャリッジリターンとラインフィード文字が両方とも除去された連続ファイルが生成される。Windows バイナリモード転送では、キャリッジリターンとラインフィード文字が保持されたストリームファイルが生成されます。これはSFTPのバイナリモード転送と全く同じ挙動です。OpenVOSからWindows ASCIIモード転送では、各行末にキャリッジリターンとラインフィード文字が付加されたファイルが生成されます。一方、バイナリモード転送では各行末にラインフィード文字のみが付加されたファイルが生成されます。 これもSFTPのバイナリモード転送と同様です。FTP RFCでは行の終端方法について詳細に規定されていますが、要するにASCIIモードではクライアントとサーバー双方がローカルOSに合わせて行を自由に終端できるのに対し、バイナリモードではそのような裁量権がクライアントとサーバーに与えられていないということです。

© 2024 ストラタス・テクノロジーズ