動的メモリ割り当てで readlink を使用する方法
質問
問題:
Linux マシン上で、リンクのターゲット文字列を読み取りたいと考えています。ドキュメントから、次のコードサンプルを見つけました(エラー処理なし)。
struct stat sb;
ssize_t r;
char * linkname;
lstat("<some link>", &sb);
linkname = malloc(sb.st_size + 1);
r = readlink("/proc/self/exe", linkname, sb.st_size + 1);
問題は、私のシステム上のリンクに対して sb.st_size が 0 を返すということです。
では、このようなシステムではどのようにして readline に動的にメモリを割り当てるのでしょうか?
どうもありがとう!
考えられる解決策の 1 つ:
今後の参考のために。jilles が作成したポイントを使用すると、次のようになります。
struct stat sb;
ssize_t r = INT_MAX;
int linkSize = 0;
const int growthRate = 255;
char * linkTarget = NULL;
// get length of the pathname the link points to
if (lstat("/proc/self/exe", &sb) == -1) { // could not lstat: insufficient permissions on directory?
perror("lstat");
return;
}
// read the link target into a string
linkSize = sb.st_size + 1 - growthRate;
while (r >= linkSize) { // i.e. symlink increased in size since lstat() or non-POSIX compliant filesystem
// allocate sufficient memory to hold the link
linkSize += growthRate;
free(linkTarget);
linkTarget = malloc(linkSize);
if (linkTarget == NULL) { // insufficient memory
fprintf(stderr, "setProcessName(): insufficient memory\n");
return;
}
// read the link target into variable linkTarget
r = readlink("/proc/self/exe", linkTarget, linkSize);
if (r < 0) { // readlink failed: link was deleted?
perror("lstat");
return;
}
}
linkTarget[r] = '\0'; // readlink does not null-terminate the string
解決
POSIXによると、シンボリックリンクのst_size
フィールドは、リンク内のパス名の長さに設定する必要があります('\0'
なし)。ただし、Linux上の/proc
ファイルシステムはPOSIXに準拠していません。(特定のファイルを一度に1バイトずつ読み取る場合など、これよりも多くの違反があります。)
特定のサイズのバッファを割り当て、readlink()
を試して、バッファが十分に大きくない場合(readlink()
がバッファに収まるだけのバイト数を返す)、バッファが十分に大きくなるまで、より大きなバッファで再試行できます。
または、PATH_MAX
を使用して、コンパイル時定数ではないシステムやパス名がそれより長くなる可能性があるシステムへの移植性を破ることができます(POSIXでも許可されています)。
他のヒント
他の回答ではそれについて言及されていませんが、 realpath
POSIX.1-2001 で指定されている、まさに必要なことを実行する関数です。
char *realpath(const char *path, char *resolved_path);
マンページから:
実パス() すべてのシンボリック リンクを展開します /./、/.../およびextra '/'文字への参照をパスで指名したヌル終端文字列の文字を解決して、標準化された絶対パス名を生成します。
realpath
必要に応じて、動的メモリ割り当ても処理します。再度、マンページからの抜粋:
Resolved_Pathがnullとして指定されている場合、RealPath()はmalloc(3)を使用して、up fath_maxバイトのバッファーを割り当てて解決されたパス名を保持し、このバッファーへのポインターを返します。発信者は、無料(3)を使用してこのバッファーを扱う必要があります。
シンプルで完全な例としては、次のようになります。
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
int
resolve_link (const char *filename)
{
char *res = realpath(filename, NULL);
if (res == NULL)
{
perror("realpath failed");
return -1;
}
printf("%s -> %s\n", filename, res);
free(res);
return 0;
}
int
main (void)
{
resolve_link("/proc/self/exe");
return 0;
}
st_sizeは/ procで正しい答えを出しません。
代わりに、PATH_MAXまたはpathconf(_PC_PATH_MAX)バイトをmallocできます。ほとんどの場合、これで十分です。それより長いパスを処理できるようにしたい場合は、ループ内でreadlinkを呼び出し、readlinkの戻り値がバッファーが短すぎることを示している場合はバッファーを再割り当てできます。ただし、他の多くのPOSIX関数は、単にPATH_MAXで十分であると想定していることに注意してください。
st_size
がゼロである理由について少し戸惑っています。 POSIXごと:
シンボリックリンクの場合、st_modeメンバーには、ファイルタイプマクロとともに使用するときに意味のある情報が含まれている必要があります。 st_modeのファイルモードビットは指定されていません。構造体メンバーst_ino、st_dev、st_uid、st_gid、st_atim、st_ctim、およびst_mtimは意味のある値を持ち、st_nlinkメンバーの値はシンボリックリンクへの(ハード)リンクの数に設定されます。 st_sizeメンバーの値は、終了ヌルバイトを含まないシンボリックリンクに含まれるパス名の長さに設定する必要があります。
出典: http://pubs.opengroup.org/onlinepubs/9699919799/関数/lstat.html
st_size
が機能しない場合、唯一の選択肢は、バッファを動的に割り当て、readlink
の戻り値がバッファサイズと等しい限り、バッファのサイズを大きくし続けることだと思います。
readlink(2)
のマンページには、バッファが小さすぎるとサイレントに切り捨てられると書かれています。本当に制限がないことを望む場合(そして追加の作業にいくらかのコストを払ってもかまわない場合)、指定された割り当てサイズから始めて、それを増やし続け、readlink
呼び出しを再試行できます。次のreadlink
の呼び出しで、最後の反復で行ったのと同じ文字列が返されたときに、バッファの拡張を停止できます。
lstatで何を達成しようとしていますか?
次の方法でターゲットを取得できるはずです ジェネラコディセタグプレ
ファイル名のサイズの長さを取得しようとしている場合、st_sizeがそのための適切な変数ではないと思います...しかし、それはおそらく別の質問です。