Samba共享傳輸大文件(ex:1G)失敗的問題


1:問題描述

  1.1 基本信息

  遇見這樣一個bug,路由器有USB share的功能,可將U盤內的文件通過samba和LAN端PC機中文件進行共享,測試發現小文件可正常共享,一旦文件大了(比如1G左右),window端便顯示一直在計算文件大小,最后客戶端(LAN pc)會因為服務器許久不回一個Subcommand:SET_FILE_INFO(0x0008)的報文而出現timeout,導致文件傳輸失敗.

過了一段時間后客戶端發出timeout [RST] 報文(windows會彈出異常對話框取消文件傳輸)

   重要的是對於NTFS的格式的U盤是正常的,只有FAT32格式的U盤有這個問題.

2:解決方法

  2.1 剛開始我覺得FAT32與NTFS的分區方式不同,可是了解到FAT32分區方式單個文件最大是4G,所以排除U盤本身問題,問題還是出在Samba服務器或者linux系統中(之所以懷疑linux系統是因為我在任何PC機上拷文件到此U盤都沒問題)

  2.2 然后先debug samba源碼,重點注意有關文件size和fat格式處理的相關代碼,然后再源碼中(\samba-3.0.24\source\smbd\vfs-wrap.c)看見了這個:

int vfswrap_ftruncate(vfs_handle_struct *handle, files_struct *fsp, int fd, SMB_OFF_T len)
{
    int result = -1;
    SMB_STRUCT_STAT st;
    char c = 0;
    SMB_OFF_T currpos;

    START_PROFILE(syscall_ftruncate);

    if (lp_strict_allocate(SNUM(fsp->conn))) {
        result = strict_allocate_ftruncate(handle, fsp, fd, len);
        END_PROFILE(syscall_ftruncate);
        return result;
    }

    /* we used to just check HAVE_FTRUNCATE_EXTEND and only use
       sys_ftruncate if the system supports it. Then I discovered that
       you can have some filesystems that support ftruncate
       expansion and some that don't! On Linux fat can't do
       ftruncate extend but ext2 can. */
    
    result = sys_ftruncate(fd, len);
    if (result == 0)
        goto done;
    
    /* According to W. R. Stevens advanced UNIX prog. Pure 4.3 BSD cannot
       extend a file with ftruncate. Provide alternate implementation
       for this */
    currpos = SMB_VFS_LSEEK(fsp, fd, 0, SEEK_CUR);
    if (currpos == -1) {
        goto done;
    }

    /* Do an fstat to see if the file is longer than the requested
       size in which case the ftruncate above should have
       succeeded or shorter, in which case seek to len - 1 and
       write 1 byte of zero */
    if (SMB_VFS_FSTAT(fsp, fd, &st) == -1) {
        goto done;
    }

#ifdef S_ISFIFO
    if (S_ISFIFO(st.st_mode)) {
        result = 0;
        goto done;
    }
#endif

    if (st.st_size == len) {
        result = 0;
        goto done;
    }

    if (st.st_size > len) {
        /* the sys_ftruncate should have worked */
        goto done;
    }

    if (SMB_VFS_LSEEK(fsp, fd, len-1, SEEK_SET) != len -1) goto done; 
    if (SMB_VFS_WRITE(fsp, fd, &c, 1)!=1)
        goto done;

    /* Seek to where we were */
    if (SMB_VFS_LSEEK(fsp, fd, currpos, SEEK_SET) != currpos)
        goto done;
    result = 0;

  done:

    END_PROFILE(syscall_ftruncate);
    return result;
}

   紅色的兩個函數是關鍵,在調試中發現,sys_ftruncate和SMB_VFS_WRITE在創建大一點的文件時會耗費很多的時間,這也是服務器遲遲不回復客戶端SET_FILE_INFO請求的原因。所以將這兩個函數屏蔽之后就正常了.

  也可以不屏蔽sys_ftruncate,可以將kernel的ftruncate函數實現修改一下:\linux-3.14\fs\fat\file.c 中函數fat_setattr

if (attr->ia_valid & ATTR_SIZE) {
        if (attr->ia_size > inode->i_size) {//if current size grenter than old size,extend it
        // if we allow this, fat ftruncate extend large size will cause samba wait a long long time.
        #if 1
            error = -EPERM;
            goto out;
        #else
            error = fat_cont_expand(inode, attr->ia_size);
            if (error || attr->ia_valid == ATTR_SIZE)
                goto out;
            attr->ia_valid &= ~ATTR_SIZE;
        #endif
        }
    }

  這樣也可以解決問題.

 總結:

  不管是FAT32還是NTFS都是先接收數據然后寫入到文件系統中,所以samba使用ftruncate函數擴展文件到文件系統中,我猜測可能是FAT32是把文件一次性使用fat_setattr函數寫到文件系統中(大文件導致寫入的時間過長),而NTFS是收到多少寫入多少.這樣導致FAT32不行而NTFS可行.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM