在CentOS 5.5上使用sed遇到的一個bug

在 CentOS 5.5 上使用 sed 遇到一個bugphp

$ echo AAA > config
$ ln -s config cfg
$ sed -i 's/AAA/aaa/' cfg
sed: ck_follow_symlink: couldn't lstat c/config: No such file or directory

這個bug發生在 sed -i do_sth symbolic_links_without_slash 時app

下載它的源代碼 https://google-search-appliance-mirror.googlecode.com/files/sed-4.1.5-5.fc6.src.rpm函數

解壓開能夠看到redhat在sed-4.1.5上打上了3個補丁google

sed-4.1.5-bz185374.patchspa

sed-4.1.5-relsymlink.patch指針

sed-4.1.5-utf8performance.patchcode


redhat爲何要爲sed打上補丁呢,這源於另一個問題,當sed -i do_sth file時實際的過程是這樣的orm

sed do_sth file > tmp
mv tmp file

那麼問題來了,若是file是指向文件file2的軟鏈接,sed -i do_sth file 的結果是:字符串

sed "剪短" 了 file 到 file2 的軟鏈接
file2 的內容不會改變
file 成爲一個file2的副本 它的內容改變了 (若是do_sth會修改)

這自己不是一個大問題 (算 sed 的一個 "小坑"吧)it

不過有人試圖解決這個問題, 因此就有了 sed-4.1.5-bz185374.patch 

這個補丁裏面新增了一個函數 ck_follow_symlink 

它核心的地方是使用readlink

好比 文件 a -> b 文件 b -> c

ck_follow_symlink可以成功的尋跡到最終的文件 a -> c

不過 ck_follow_symlink 在有種狀況下不能正常工做

文件 a -> ../b 而文件 b -> c

也就是說 a -> ../c 而 ck_follow_symlink 把它理解爲了 a -> c

也就是說新引入了一個問題


爲了解決這個新問題,因此又有了補丁sed-4.1.5-relsymlink.patch

這個補丁就是爲了解決 a -> ../b 且 b -> c 這樣的場景

解決方案是這樣的:(補丁片斷)

  //buf 是記錄當前文件名的buffer  
  err = readlink (buf, buf2, bufsize);
  buf2 [err] = '\0';
  if (buf2[0] != '/')
    {
      dir = dirname (buf);    // dir part of orig path
      int len = strlen (dir); // orig path len
      buf[len] = '/';
      strncpy (buf+len+1, buf2, bufsize - len - 1);

那麼問題來了,dirname 是怎麼工做的

char ch[] = "/tmp/123";
char * p = dirname(ch);

dirname(ch) 的行爲是 把 ch[4] 從 '/' 改爲了 0 並把ch的地址做爲返回值

但當 char ch[] = "tmp"時 dirname(ch) 的行爲又是如何呢?

答案是 不對ch作任何修改 dirname(ch) 返回一個指針 這個指針指向一個常字符串 "." 

這裏對dirname的使用是錯誤的, 他錯誤的認爲dirname必定會去修改入參並返回入參地址

因此對dirname的錯誤使用引入了文章開頭的那個bug

這個bug在存在於CentOS/RHEL/Fedora這一支的某些版本中, 有無最終修復未考證


對與sed主線,它沒有這個問題,對於sed -i 做用與軟鏈接 "剪短" 軟鏈接這個問題

在4.1.5以後的 sed 加了一個新參數--follow-symlinks,它能夠保證sed -i 時不「剪斷」軟鏈接

沒有這個參數時 sed -i 的行爲和以前版本一致

相關文章
相關標籤/搜索