2012-07-04 27 views
3

我有一個嵌入式環境,用戶可能會插入或移除USB閃存驅動器。我想知道驅動器是否已被移除,或者當我嘗試寫入驅動器時是否存在其他問題。但是,Linux只是將信息保存在緩衝區中,並返回時沒有指示錯誤。
我使用的計算機附帶2.4.26內核和libc 2.3.2。
我安裝的驅動器是這樣的:
i = mount(MEMORY_DEV_PATH, MEMORY_MNT_PATH, "vfat", MS_SYNCHRONOUS, NULL);
這一工程:
50:/root # mount
/dev/scsi/host0/bus0/target0/lun0/part1 on /mem type vfat (rw,sync)
50:/root #
如何檢測C中的文件寫入錯誤?

後來,我嘗試將文件複製到:

int ifile, ofile; 
ifile = open("/tmp/tmpmidi.mid", O_RDONLY); 
if (ifile < 0) 
{ 
    perror("open in"); 
    break; 
} 
ofile = open(current_file_name.c_str(), O_WRONLY | O_SYNC); 
if (ofile < 0) 
{ 
    perror("open out"); 
    break; 
} 
#define BUFSZ 256 
char buffer[BUFSZ]; 
while (1) 
{ 
    i = read(ifile, buffer, BUFSZ); 
    if (i < 0) 
    { 
     perror("read"); 
     break; 
    } 
    j = write(ofile, buffer, i); 
    if (j < 0) 
    { 
     perror("write"); 
     break; 
    } 
    if (i != j) 
    { 
     perror("Sizes wrong"); 
     break; 
    } 
    if (i < BUFSZ) 
    { 
     printf("Copy is finished, I hope\n"); 
     close(ifile); 
     close(ofile); 
     break; 
    } 
} 

如果這個代碼片段是用寫保護的USB存儲器執行,結果是

Copy is finished, I hope 

中來自控制檯上的內核的一系列錯誤消息。
我相信如果我只是移除USB驅動器(不卸載它),也會發生同樣的情況。
我也玩弄devfs。我想出瞭如何讓它自動掛載驅動器(使用REGISTER事件),但是當我拔出內存時,它似乎永遠不會觸發UNREGISTER。
如何確定我的程序是否已成功創建文件?

更新7月4日: 這是一個愚蠢的疏忽,我不檢查close()的結果。不幸的是,該文件可以無錯地關閉。所以這沒有幫助。那麼fsync()呢?這聽起來像個好主意,但也沒有發現錯誤。
如果我有這樣的事情,那麼/ sys中可能會有一些有趣的信息。我相信直到2.6時纔會添加。
關於我的閃存驅動器質量的評論可能是合理的。這是早期的一個。事實上,現在寫保護開關似乎非常罕見。
我想我必須使用過度殺傷選項:創建一個文件,卸載&重新掛載驅動器,並檢查文件是否存在。如果這不能解決我的問題,那麼真的搞砸了!請注意:確保您嘗試創建的文件尚未存在!順便說一下,這確實是一個C++程序。您可以通過.c_str()來說明我爲了簡單而打算編輯的內容。

+0

您是否嘗試過使用C標準庫I/O函數('fopen','fread','fwrite'等)而不是POSIX? –

+0

您可能還想檢查關閉的錯誤。 –

回答

0

這是POSIX的做事方式。如果write的返回值是-1您肯定知道某些事情發生了可怕的錯誤。但是,如果write返回0,則也可能出錯。檢查errno變量以查看它是否與以下顯示的預定義寫入錯誤之一相匹配:http://www.kernel.org/doc/man-pages/online/pages/man2/write.2.html

+0

OP使用C而不是C++。 iostream是C++標準庫的一部分。 –

+0

@EmileCormier已修復! – DynamiteReed

+0

不完全。 :) OP使用POSIX文件I/O函數,而不是C標準庫函數。 –

0

如果您找不到解決方案,那麼可以嘗試這種醜陋的破解方法。在寫入並關閉輸出文件之後,您可以簡單地嘗試在讀取模式下打開它並檢查它是否具有合適的大小。如果您確實希望確保它具有正確的內容,則可以驗證它是否與您剛剛寫入的文件具有相同的校驗和。這假設操作系統將直接從USB驅動器讀取文件,而不是某種緩存。

2

如果你的應用程序要一個或多個文件保存到USB記憶棒,並讓用戶幾乎立即拔出棒的小LED指示燈熄滅後,您需要

  1. mount()的USB堅持只是寫在文件

    之前,您不需要安裝SYNC這一點,與劣質USB有助於使用正確的代碼

    堅持

  2. 保存文件

    您在C低級I/O上的嘗試相當不正確。特別是,允許​​任何read()write()返回短計數,在1和所請求的大小之間的任何正值(包括端值),而不指示任何類型的錯誤。

  3. fsync()上的USB棒

    文件驗證它返回成功。如果確實如此,那麼你知道數據已經打到USB記憶棒。

  4. fsync()包含文件的USB棒

    驗證它返回成功的目錄。如果確實如此,那麼您知道文件元數據已經擊中USB棒,並且現在即使用戶將USB棒拔出,文件也應該可以訪問。

  5. umount() USB記憶棒

    這將阻止,直到USB棒是準備被斷開,所以你的應用程序看起來應該像它保存到文件,直到umount()回報。

    如果你做了fsync()以上,umount()應該是非常直接的。根據文件系統的不同,內核可能會做一些簿記工作,但在任何情況下都不需要很長時間。

其他任何東西根本不可靠。您可以通過在同步訪問中安裝VFAT分區來做出某些假設,但基本上只是手動操作。

如果您不想以root權限運行您的應用程序,則始終可以編寫一個簡單的特權服務來管理掛載和卸載。如果您懷疑某個時候您可能需要多個應用程序,我會非常熱情地推薦它,因爲只有集中的貼片機/非貼片機才能知道媒體何時準備就緒。 (如果另一個應用程序同時向同一個USB媒體寫入,它可能會延遲返回「unmounted」消息。順便說一下,當你不需要同步安裝USB媒體時,效果會很好)。在/var/run/中使用unix域數據報套接字,也許/var/run/mounter.socket,用於進程間通信。

最後,如果你的Linux內核的配置是否正確,你有/sys/分區安裝,您可以掃描所有/sys/block/sd?/目錄可移動介質:

  • /sys/block/sd?/removable將包含一個非零十進制數串
  • /sys/block/sd?/size包含設備大小以512字節爲單位,十進制數字字符串
  • /sys/block/sd?/device/vendor包含供應商名稱作爲字符串
  • 包含型號名稱作爲字符串

這些條目是硬件級別的,並且在USB棒連接並通電時可用;無論是否安裝都無關緊要。如果/當用戶拔出USB棒時,整個設備目錄樹將立即消失。

1

簡答:問題出在操作系統和/或USB驅動器上。即使設置了 寫保護開關,Linux 也會(有時候?)愉快地將USB驅動器設置爲可寫。然而,當它實際上試圖寫入 時,驅動器拒絕並且這或多或少地作爲 有缺陷的驅動器處理。 (我不確定爲什麼發生這種情況我的猜測是 閃存驅動器沒有報告它對操作系統的只讀內存,這個 可能取決於你使用的閃存驅動器的品牌和型號)

我可以通過3.2.0-26 內核(特別是Ubuntu 12)上的(非常舊的)USB密鑰獲得相同的行爲。我可以毫無困難地將 寫保護的USB密鑰讀寫,如果我將 文件複製到其中,「cp」不會投訴。該文件甚至可能出現在目錄中(因爲緩衝區爲 ),但實際上沒有任何文件被寫入。我在syslog中收到了很多錯誤消息,其中有 。

如果我是你,我會嘗試實際寫入閃存驅動器,並確保它在成功假設驅動器實際可寫之前成功。 具體來說,我想:

  1. 將驅動器掛載爲可寫。

  2. 使用唯一名稱在驅動器上創建一個新文件。

  3. 卸載驅動器清空緩衝區。

  4. 重新安裝驅動器。

  5. 檢查新文件是否仍然存在幷包含您寫入的數據。

  6. 刪除測試文件。

我不是足夠新的硬件和驅動程序的狀態告訴你 如果有檢測從API不可寫硬盤的方式 - 有可能 可以,但我不知道。但即使有,這種情況下也會檢測到 行爲不正常的USB驅動器。

更新:

我做了些研究和事實證明,如果寫保護開關設置的fsync()上的文件句柄做會失敗。因此,我收回了上述建議。相反,這裏是我的測試程序:

#include <sys/types.h> 
#include <sys/stat.h> 
#include <fcntl.h> 

#include <stdio.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <string.h> 

int 
main(int argc, char *argv[]) { 
    char *path; 
    int f; 
    size_t stat; 
    const char *message = "Hello, world.\n"; 

    if (argc != 2) { 
     printf("Need filename\n"); 
     exit(1); 
    } 

    path = argv[1]; 

    f = open(path, O_CREAT | O_WRONLY | O_SYNC, S_IRWXU); 
    if (f < 0) { 
     perror("open out"); 
     exit(1); 
    }/* if */ 

    stat = write(f, message, strlen(message)); 
    if (stat < 0) { 
     perror("write"); 
     exit(1); 
    }/* if */ 

    stat = fsync(f); 
    if (stat) { 
     perror("fsync"); 
     exit(1); 
    } 

    stat = close(f); 
    if (stat) { 
     perror("close"); 
     exit(1); 
    }/* if */ 

    printf("(Apparently) successfully wrote '%s'\n", path); 
    return 0; 
} 

如果設備不可寫,這應該在fsync()調用上失敗。

+0

(評論已收回) – rich

2

如果要檢測所有寫錯誤,您需要檢查絕不僅僅是write()返回代碼更 - 你還必須調用fsync()(和檢查返回值),並檢查通過close()報告的錯誤。

+0

+1「檢查close()報告的錯誤」那個。人們會認爲沒有必要,因爲當關閉文件失敗時,無論如何你可以做些什麼。然而,這個Q只是你需要這樣做的一個例子。文檔明確地稱它也沒有做到「嚴重的編程錯誤」。 – Damon