2017年3月4日 星期六

Glusterfs Crypt Ftruncate Extend File Size Trace Code Flow



ftruncate expend file fop
  • finodelk (whole file)
  • fgetxattr(get trusted.glusterfs.crypt.att.size value)
  • readv(rmw readv head block)
  • writev(write head block)
  • writev(write full block loop )
  • readv(rmw readv tail block)
  • writev(write tail block)
  • fsetxattr(update new file size)
  • finodelk(unlock file) 



finodelk: lock whole file
  1. Allocate GF_FOP_FTRUNCATE local datastruct
  2. Init local xattr dict
  3. Get inode info
  4. Set crypt algo info
  5. Set lock info (the lock whole file)
    • lock.l_len    = 0;
    • lock.l_start  = 0;
    • lock.l_type   = F_WRLCK;
    • lock.l_whence = SEEK_SET;
  6. 呼叫STACK_WIND finodelk去加鎖整個檔案





fgetxattr: get original file size
  1. Get FSIZE_XATTR_PREFIX attribution value
    1. FSIZE_XATTR_PREFIX = trusted.glusterfs.crypt.att.size
  2. 呼叫STACK_WIND gsetxattr去獲得目前的file size


readv: read the head block data
  1. Get extract regular file size from FSIZE_XATTR_PREFIX attribution
  2. Determine shrink file or expand file
  3. Expand file
  4. Prepare to submit a hole for file
  5. Set expand file info about how many block need to expand(set_config_offsets)
    1. dtype = HOLE_ATOM(塞空洞進去)
    2. block size: 4096
    3. expand一段size會被分成三個部份
      1. head block(從原本檔案的offset所屬的block)
      2. full blocks (中間完整的block)
      3. tail block(最後expand完offset所屬的block)
    4. off_in_head: head block offset
    5. off_in_tail: tail block offset
    6. nr_full_blocks: full block count
    7. acount: total block count(head block+full block+tail block)
  1. Allocate and setup vector for hole
    1. allocate 3 blocks in pool(head, full, tail) for data
    2. allocate a array avec and put block into avec
      1. avec[0].iov_base = pool[0] 分給head block並且將off_in_head到block底這一段利用memset()設為0
      2. avec[2].iov_base = pool[2] 分給tail block並且從block起始到off_in_head這一段利用memset()設為0
      3. avec[1].iov_base = pool[1] 分給full block
    3. 將這些avec資訊放到&local->hole_conf中
  2. Sumit the hold
    1. 這裡有三種case要處理
      1. head block
      2. full block
      3. tail block
    2. 首先會先處理head block(submit_head)
    3. 利用STACK_WIND的方式呼叫crypt的readv函式(crypt_readv)
    4. crypt_readv中
      1. allocate a new local for GF_FOP_READ
      2. 設定要讀的head block資料的offset及相關block(通常只有tail block, head block和full block都沒有)(set_config_offsets)
      3. 呼叫STACK_WIND去讀從head block到off_in_head的資料


writev: write zero data per block(head block)
  1. Set data info from read, type: DATA_ATOM(set_config_offsets)
  2. Correct config params with real file size and actual amount of bytes read
  3. Get data from vec buffer
    1. allocate avec and block(放資料的地方)
    2. 利用memcpy將資料copy到block
    3. 將block放到avec[]中
    4. copy了超過了原本預期大小的資料
  4. decrypt iov data(解密依照所讀的大小)
  5. Fix iov len to correct len
  6. 利用STACK_UNWIND_STRICT回到呼叫crypt_readv的call function(rmw_hole_head)
  7. 此時回到GF_FOP_FTRUNCATE local datastruct
  8. 利用memcpy將vec[0] copy到partial[0]中(vec表示剛剛從readv讀到的資料, partial是之前在Allocate and setup vector for hole所allocate的avec)
  9. 對於partial[0]整個block加密(針對head block加密, 之前就已經把off_in_head之後的byte都寫0了)
  10. Set new file size (包含head block的block size, like 8192(2 block))
  11. 呼叫STACK_WIND writev將資料往下寫


writev: write zero data per block(full block)
  1. Update new file params(cur_file_size)
  2. 又回到Submit hole
  3. 這次是呼叫submit_full來處理full block的寫入
  4. 利用memset把0填入avec[1] (full block)
  5. 加密avec[1]
  6. Set new file size (包含head block的block size, like 12288(2 block))
  7. 呼叫STACK_WIND將資料往下寫
  8. 一直loop呼叫直到tail block


readv: read the tail block data
  1. Update new file params(cur_file_size)
  2. 又回到Submit hole
  3. 執行submit_partial(), type=TAIL_ATOM
  4. 利用STACK_WIND的方式呼叫crypt的readv函式(crypt_readv)
  5. crypt_readv中
    1. allocate a new local for GF_FOP_READ
    2. 設定要讀的head block資料的offset及相關block(通常只有tail block, head block和full block都沒有)(set_config_offsets)
    3. 呼叫STACK_WIND去讀從tail block到off_in_tail的資料


writev: write zero data per block(tail block)
  1. return 0個byte len也是0(因為檔案沒有那麼大)
  2. 利用STACK_UNWIND_STRICT回到呼叫crypt_readv的call function(rmw_hole_tail)
  3. 直接將partial[3]也就是tail block以off_in_tail的len寫到下一層(之前在init iov也就是partial的時候就將tail block起頭到offset的範圍初始成0了)
  4. 加密partial[3]的資料
  5. Set new file size
  6. 呼叫STACK_WIND writev將資料往下寫


fsetxattr: set new file size
  1. Update file size
  2. Set FSIZE_XATTR_PREFIX(trusted.glusterfs.crypt.att.size) new file size
  3. 呼叫STACK_WIND fsetxattr將新的file size往下寫


finodelk: lock whole file
  1. Set unlock info(unlock whole file)
    • lock.l_type   = F_UNLCK;   
    • lock.l_whence = SEEK_SET;
    • lock.l_start  = 0;
    • lock.l_len    = 0;
  2. 呼叫STACK_WIND finodelk去解鎖



心得:
ftruncate在linux只是一個改變file size的指令, 通常很快就結束, 因為他只需要去改變檔案的metadata就可以了. 當你去讀取大於原本file size的區塊, linux kernel通常會回傳0給你.但crypt xlator裡面就沒有那麼簡單. 因為crypt xlator是加密的xlator, 所有寫到OS的資料都會加密. 當你去讀取大於原本file size的區塊, 若linux kernel回傳0給crypt xlator, 在經過解密回傳上去就可能不是0. 所以他採用了一個很簡單的方式來解決, 他在ftruncate的時候就直接幫你寫0, 之後你若讀取他回傳解密後就會是0. 符合一般linux file system的操作.

 








沒有留言:

張貼留言