OpenSync 功能說明與系統分析 === ## 專案狀態 - OpenSync on [Github](https://github.com/Neol-d2022/OpenSync-se106a) - Travis-CI: [![Build Status](https://travis-ci.org/Neol-d2022/OpenSync-se106a.svg?branch=master)](https://travis-ci.org/Neol-d2022/OpenSync-se106a) ## 目錄 [TOC] ## 建置 1. `$ automake` 建置開發環境 2. `$ ./configure` 設定 ==Makefile== 3. - `$ make` 建置專案 - `$ make test` 測試專案 - `$ make dist` 發布專案(建立壓縮檔) - `$ make distcheck` 測試發布檔案 ## 功能說明 - OpenSync的預期功能是達成在兩台不同機器上,同步資料夾的效果。 - ![](https://i.imgur.com/X2afshp.png) - 相較於Dropbox、MEGASync、Google Drive等,只能同時同步一個資料夾。OpenSync能處理多個同步資料夾,且針對每個資料夾可以個別設定與不同的遠端機器進行同步。 - 本專案為開放原始碼專案。所有原始碼均公開在 [Github](https://github.com/Neol-d2022/OpenSync-se106a) 網站上。 ## 系統分析 - 本專案使用 **C 語言** 撰寫。 ### 記憶體管理工程 **([已完工](/SyFbyYYAb#%E8%A8%98%E6%86%B6%E9%AB%94%E7%AE%A1%E7%90%86%E6%A8%A1%E7%B5%84--mmh-))** - 將 ==stdlib.h== 內與記憶體管理相關的函數重新包裝,讓程式要求記憶體,但發生記憶體不夠用的時候,直接終止程式而不是傳回空指標。 - 提供除錯功能。偵測程式有無記憶體洩漏的情況。 - ![](https://i.imgur.com/J1HG9ET.png) ### 檔案樹工程 **([已完工](/SyFbyYYAb#%E6%AA%94%E6%A1%88%E6%A8%B9%E6%A8%A1%E7%B5%84--filetreeh-))** - 先將同步資料夾的路徑寫死。 - `$ ./syncfolder` - 程式啟動時讀取目前目錄下的 ==syncfolder== 底下所有的檔案及資料夾,並建立一個樹狀結構來儲存。 - 使用 ==dirent.h== 這個標頭檔,可以列舉資料夾下面的檔案。 - 用`stat()`函數辨別某個路徑是資料夾還是一般檔案。 - ![](https://i.imgur.com/WNHM2vu.png) ### 網路通訊工程 - 使用TCP socket建立連線。 - 定義應用層通訊協定,讓程式能夠互相通訊。 - ![](https://i.imgur.com/DDL51u5.png) ### 檔案樹比對工程 **([已完工](/SyFbyYYAb#%E6%AA%94%E6%A1%88%E6%A8%B9%E6%A8%A1%E7%B5%84--filetreeh-))** - 讓程式能夠收送檔案樹。 - 穰程式能夠比對出檔案樹的差異。 - ![](https://i.imgur.com/jXyHVHI.png) ### 網路通訊工程-2 - 讓程式能夠收送檔案。 - ![](https://i.imgur.com/nZTcnXI.png) ### 檔案監視工程 - 讓程式能夠監視檔案系統。 - **盡可能跨平台。** - 一有更動立刻更新本地檔案樹,與遠端交換檔案樹,最後做出差異比對。 - 遠端也有可能隨時送來更新的檔案樹,此時也要做差異比對。 ### 檔案同步工程 - 根據差異計算出的差異判斷出: - 本地檔案新增 - 遠端檔案新增 - 本地檔案修改 - 遠端檔案修改 - 本地擋案移動 - 遠端檔案移動 - ...等 - 並做出適當的動作 - 推送的本地剛新增檔案至遠端 - 從遠端取得剛新增的檔案 - 推送的本地修改後的檔案至遠端 - 從遠端取得修改後的檔案 - 告知遠端(交換檔案樹後即可得知) - 將本地檔做移動,與遠端同步 - ...等 ### 設定檔工程 - 讀取設定檔,程式執行時分配每一個thread監視一個同步資料夾,且每一個資料夾皆可以指定遠端機器路徑。 - 使用POSIX中的`pthread` ### 中央伺服器工程 - 將OpenSync中網路通訊的部分,從Peer-to-Peer的模式變成Client-Server。並在Server記錄每一個同步資料夾的版本變更。 ### 客戶憑證工程 - 中央伺服器如何確認客戶端合法使用者?客戶端如何確認伺服器沒有被偽裝? ### 流量加密工程 - 針對透過socket傳輸的資料串流,套用加密演算法,以保護資料機密性。 ### 開派對慶祝工程完畢。 ## 技術手冊 ### 記憶體管理模組 ( ==mm.h== ) 1. 名稱 **mm - memory management functions** 1. 使用方法 ```c #include "mm.h" ``` 1. 函式列表 ```c /* Request a memory block with s bytes */ void *Mmalloc(size_t s); /* Relocate memory block p to s bytes */ void *Mrealloc(void *p, size_t s); /* When the system is out of memory, the functions above will immediately crash the program, instead of returning NULL */ /* Release memory block p */ void Mfree(void *p); /* DEBUG. Return the number of allocated memory blocks */ size_t MDebug(); ``` 1. 測試 - 使用模組測試功能 ```c= #include "mm.h" #include "mm_test.h" // add this line int main() { int mm_test(void); // add this line return 0; } ``` - 回傳0代表測試成功,否則代表失敗。 --- ### 字串處理模組 ( ==string.h== ) 1. 名稱 **string - string operation functions** 1. 使用方法 ```c #include "strings.h" ``` 1. 函式列表 ```c /* Duplicate string str. Must be released by call to Mfree(). */ char *SDup(const char *str); /* Concatenate two strings. Must be released by call to Mfree(). */ char *SConcat(const char *s1, const char *s2); /* Concatenate multiple strings. Must be released by call to Mfree(). */ char *SMConcat(size_t n, ...); /* Concatenate multiple strings. Must be released by call to Mfree(). */ char *SMConcatA(size_t n, const char **array); ``` 1. 測試 - 使用模組測試功能 ```c= #include "string.h" #include "strings_test.h" // add this line int main() { int string_test(void); // add this line return 0; } ``` - 回傳0代表測試成功,否則代表失敗。 --- ### 可轉換容器模組 ( ==transformcontainer.h== ) - 此模組是一個子專案,函式輸出及測試請參考它的 [Github](https://github.com/Neol-d2022/transformcontainer)。 --- ### 檔案校驗和模組 ( ==crc32.h== ) 1. 名稱 **crc32 - file checksum functions** 1. 使用方法 ```c #include "crc32.h" ``` 1. 函式列表 ```c /* 計算檔案的校驗和,檔案必須先被開啟。計算完畢之後不會關閉檔案。 */ int Crc32_ComputeFile(FILE *file, uint32_t *outCrc32); /* 計算一塊記憶體區段的校驗和。 */ /* 呼叫時 inCrc32 應為0 */ uint32_t Crc32_ComputeBuf(uint32_t inCrc32, const void *buf, size_t bufLen); ``` 1. 測試 - 本模組沒有測試功能。 注: 參考來源: **網際網路**。 --- ### 記憶體處理模組 ( ==mb.h== ) 1. 名稱 **mb - memory block management functions** 1. 使用方法 ```c #include "mb.h" ``` 1. 結構定義 ```c typedef struct { void *ptr; /* 指標。指向一塊記憶體區段的起始位置。 */ size_t size; /* 這塊記憶體區段的大小。 */ } MemoryBlock_t; /* 記憶體區段 */ ``` 1. 函式列表 ```c /* Duplicate memory block m. Must be released by call to MBfree(). */ void MDup(MemoryBlock_t *dst, const MemoryBlock_t *m); /* Concatenate two memory blocks. Must be released by call to MBfree(). */ void MConcat(MemoryBlock_t *dst, const MemoryBlock_t *m1, const MemoryBlock_t *m2); /* Concatenate multiple memory blocks. Must be released by call to MBfree(). */ void MMConcat(MemoryBlock_t *dst, size_t n, ...); /* Concatenate multiple memory blocks. Must be released by call to MBfree(). */ void MMConcatA(MemoryBlock_t *dst, size_t n, MemoryBlock_t *array); /* Release a memory block */ void MBfree(MemoryBlock_t *m); /* Write a 32-bit unsigned integer */ void MWriteU32(void *ptr, uint32_t v); /* Read a 32-bit unsigned integer */ uint32_t MReadU32(void **ptr); /* Write a 64-bit unsigned integer */ void MWriteU64(void *ptr, uint64_t v); /* Read a 64-bit unsigned integer */ uint64_t MReadU64(void **ptr); /* Write a string to memory block. Must be released by call to MBfree().*/ void MWriteString(MemoryBlock_t *dst, const char *s); /* Read a string from memory block. Must be released by call to Mfree(). when pass an inappropriate argument, a NULL is returned.*/ char *MReadString(void **ptr, size_t *maxLength); ``` 1. 測試 - 本模組沒有測試功能。 --- ### 檔案樹模組 ( ==filetree.h== ) 1. 名稱 **filetree - file tree management functions** 1. 使用方法 ```c #include "filetree.h" ``` 1. 結構定義 ```c typedef struct { size_t size; /* 檔案大小 */ time_t timeLastModification; /* 檔案最後修改時間 */ uint32_t crc32; /* 存放檔案CRC32檢查碼 */ uint32_t version; /* 檔案版本 */ } FileNodeTypeFile_t; /* 檔案節點 */ typedef struct { struct FileNode_struct_t **children; /* 這個資料結下的節點 */ size_t childrenLen; /* 這個資料結下的節點數量 */ } FileNodeTypeFolder_t; /* 資料夾節點 */ typedef struct FileNode_struct_t { union { FileNodeTypeFile_t file; FileNodeTypeFolder_t folder; }; /* 節省記憶體空間 */ char *nodeName; /* 檔名 */ char *fullName; /* 完整路徑 */ struct FileNode_struct_t *parent; /* 上一層的資料夾節點 */ unsigned int flags; /* 旗標 */ } FileNode_t; /* 節點 */ typedef struct { char *basePath; /* 檔案樹的路徑 */ FileNode_t ****indexes; /* 總索引表 */ FileNode_t **baseChildren; /* 第一層節點 */ FileNode_t **totalFiles; /* 所有檔案 */ FileNode_t **totalFolders; /* 所有資料夾 */ size_t baseChildrenLen; /* 第一層節點數量 */ size_t totalFilesLen; /* 所有檔案數量 */ size_t totalFoldersLen; /* 所有資料夾數量 */ } FileTree_t; /* 檔案樹 */ typedef struct { FileNode_t *from; /* 被更改的節點 */ FileNode_t *to; /* 更改後的節點 */ } FileNodeDiff_t; /* 節點差異結構 */ ``` 1. 函式列表 ```c /* Initialize a file tree */ void FileTreeInit(FileTree_t *t); /* Destroy a file tree */ void FileTreeDeInit(FileTree_t *t); /* Set Base Path */ void FileTreeSetBasePath(FileTree_t *t, const char *basePath); /* Scan And Create File Tree */ int FileTreeScan(FileTree_t *t); /* DEBUG. Print file tree */ void FileTreeDebugPrint(FileTree_t *t); /* File Tree to Memory Block */ void FileTreeToMemoryblock(FileTree_t *t, MemoryBlock_t *mb); /* Memory Block to File Tree, Return NULL if invalid */ FileTree_t *FileTreeFromMemoryBlock(MemoryBlock_t *mb, const char *parentPath); /* Compute CRC32 of every files under the tree */ int FileTreeComputeCRC32(FileTree_t *t); /* Compute Difference */ unsigned int FileTreeDiff(FileTree_t *t_old, FileTree_t *t_new, FileNodeDiff_t ***diff, size_t *diffLen); /* Release Object */ void FileNodeDiffRelease(FileNodeDiff_t **diff, size_t len); /* DEBUG. Print file node difference */ void FileNodeDiffDebugPrint(FileNodeDiff_t **diff, size_t len); /* */ FileTree_t *FileTreeFromFile(const char *filename, const char *syncdir); /* */ int FileTreeToFile(const char *filename, FileTree_t *ft); ``` 1. 測試 - 使用模組測試功能 ```c= #include "string.h" #include "filetree_test.h" // add this line int main() { int filetree_test(void); // add this line return 0; } ``` - 回傳0代表測試成功,否則代表失敗。 6. 補充 - 如果`FileTreeSetBasePath()`遇到不可存取的資料夾/檔案(通常是權限不足),或是無法辨別型態(既不是檔案也不是資料夾),則會忽略那個檔名並繼續,並會回傳 **最後一次** 發生的`errno`。 - 正常結束則回傳0。 - `FileTreeToMemoryblock()` 將檔案樹以一塊記憶體區段來表示,以便日後網路通訊使用。 - `MemoryBlock_t *mb` 可不必初始化;**若已經初始化,則必須先釋放。** - `FileTreeFromMemoryBlock()` 可將一塊記憶體區段轉換成檔案樹。 - `const char *parentPath` 必須指定檔案樹的**根**資料夾路徑。 - 若此記憶體區段無法表示成檔案樹,則會回傳空指標。 --- ### 專案描述模組 ( ==config.h== ) 1. 名稱 **config - package description definitions** 1. 描述 - 定義專案名稱及版本號碼。 ```c #define PACKAGE_NAME "OpenSync" #define PACKAGE_VERSION "version_number" ``` 1. 測試 - 本模組沒有測試功能。 --- ### 設定檔模組 ( ==configurer.h== ) 1. 名稱 **configurer - configure file loader for main program initialization** 1. 使用方法 ```c #include "configurer.h" ``` 1. 結構定義 ```c typedef struct { SynchronizationProtocols_t *sp; // 執行緒同步物件 char *workingFolder; // 客戶端工作資料夾(檔名自動產生) char *basePath; // 客戶端同步資料夾 char *remoteIP; // 伺服器IP位置 unsigned int remotePort; // 伺服器port號碼 unsigned int magicNumber; // 認證用magic number(防止設定檔誤設) } SynchronizationClient_t; // 客戶端 typedef struct { SynchronizationProtocols_t *sp; // 執行緒同步物件 char *workingFolder; // 伺服器工作資料夾(檔名自動產生) char *basePath; // 伺服器同步資料夾 unsigned int listeningPort; // 監聽port號碼 unsigned int magicNumber; // 認證用magic number(防止設定檔誤設) } SynchronizationServer_t; // 伺服器 typedef struct { SynchronizationProtocols_t *sp; // 執行緒同步物件 size_t nClients; // 要管理的客戶端數量 size_t nServers; // 要管理的伺服器數量 SynchronizationClient_t **clients; // 客戶端 SynchronizationServer_t **servers; // 伺服器 pthread_t *clientThreads; // 客戶端執行緒 pthread_t *serverThreads; // 伺服器執行緒 } Configuration_t; // 客戶端及伺服器設定 ``` 1. 函式列表 ```c /* Read configuration file */ int ConfigurerReadConfig(const char *filename, Configuration_t *c); /* Release all resources which associated with the configuration */ void ConfigurerRelease(Configuration_t *c); /* Print the configuration */ void ConfigurerDebugPrint(Configuration_t *c); /* Start the program by the configuration */ int ConfigurerStartup(Configuration_t *c); ``` 1. 測試 - 使用模組測試功能 ```c= #include "string.h" #include "configurer_test.h" // add this line int main() { int configurer_test(void); // add this line return 0; } ``` - 回傳0代表測試成功,否則代表失敗。 --- ### 網路通訊模組 ( ==xsocket.h== ) 1. 名稱 **xsocket - network socket functions** 1. 使用方法 ```c #include "xsocket.h" ``` 1. 函式列表 ```c /* Socket library initialization */ int socketLibInit(void); /* Socket library deinitialization */ int socketLibDeInit(void); /* Close network socket */ int socketClose(SOCKET sock); ``` 1. 測試 - 本模組沒有測試功能。 --- ### 模組 ( ==.h== ) 1. 名稱 **** 1. 使用方法 ```c #include ".h" ``` 1. 結構定義 ```c ``` 1. 函式列表 ```c ``` 1. 測試 - 使用模組測試功能 ```c= #include "string.h" #include ".h" // add this line int main() { int _test(void); // add this line return 0; } ``` - 回傳0代表測試成功,否則代表失敗。 - 本模組沒有測試功能。