diff --git a/common/seaf-utils.c b/common/seaf-utils.c index 5ef9860..df0bead 100644 --- a/common/seaf-utils.c +++ b/common/seaf-utils.c @@ -459,3 +459,18 @@ seaf_parse_auth_token (const char *auth_token) g_strfreev (parts); return token; } + +void +split_filename (const char *filename, char **name, char **ext) +{ + char *dot; + + dot = strrchr (filename, '.'); + if (dot) { + *ext = g_strdup (dot + 1); + *name = g_strndup (filename, dot - filename); + } else { + *name = g_strdup (filename); + *ext = NULL; + } +} diff --git a/common/seaf-utils.h b/common/seaf-utils.h index 4561811..2401bed 100644 --- a/common/seaf-utils.h +++ b/common/seaf-utils.h @@ -25,4 +25,7 @@ seaf_gen_notif_server_jwt (const char *repo_id, const char *username); char * seaf_parse_auth_token (const char *auth_token); +void +split_filename (const char *filename, char **name, char **ext); + #endif diff --git a/fileserver/fileop.go b/fileserver/fileop.go index 53f4801..c06bbda 100644 --- a/fileserver/fileop.go +++ b/fileserver/fileop.go @@ -878,16 +878,19 @@ func downloadZipFile(rsp http.ResponseWriter, r *http.Request, data, repoID, use rsp.Header().Set("Content-Disposition", contFileName) rsp.Header().Set("Content-Type", "application/octet-stream") + fileList := []string{} for _, v := range dirList { + uniqueName := genUniqueFileName(v.Name, fileList) + fileList = append(fileList, uniqueName) if fsmgr.IsDir(v.Mode) { - if err := packDir(ar, repo, v.ID, v.Name, cryptKey); err != nil { + if err := packDir(ar, repo, v.ID, uniqueName, cryptKey); err != nil { if !isNetworkErr(err) { log.Printf("failed to pack dir %s: %v", v.Name, err) } return nil } } else { - if err := packFiles(ar, &v, repo, "", cryptKey); err != nil { + if err := packFiles(ar, &v, repo, "", uniqueName, cryptKey); err != nil { if !isNetworkErr(err) { log.Printf("failed to pack file %s: %v", v.Name, err) } @@ -900,6 +903,39 @@ func downloadZipFile(rsp http.ResponseWriter, r *http.Request, data, repoID, use return nil } +func genUniqueFileName(fileName string, fileList []string) string { + var uniqueName string + var name string + i := 1 + dot := strings.Index(fileName, ".") + if dot < 0 { + name = fileName + } else { + name = fileName[:dot] + } + uniqueName = fileName + + for nameInFileList(uniqueName, fileList) { + if dot < 0 { + uniqueName = fmt.Sprintf("%s (%d)", name, i) + } else { + uniqueName = fmt.Sprintf("%s (%d).%s", name, i, fileName[dot+1:]) + } + i++ + } + + return uniqueName +} + +func nameInFileList(fileName string, fileList []string) bool { + for _, name := range fileList { + if name == fileName { + return true + } + } + return false +} + func parseDirFilelist(repo *repomgr.Repo, obj map[string]interface{}) ([]fsmgr.SeafDirent, error) { parentDir, ok := obj["parent_dir"].(string) if !ok || parentDir == "" { @@ -988,7 +1024,7 @@ func packDir(ar *zip.Writer, repo *repomgr.Repo, dirID, dirPath string, cryptKey return err } } else { - if err := packFiles(ar, v, repo, dirPath, cryptKey); err != nil { + if err := packFiles(ar, v, repo, dirPath, v.Name, cryptKey); err != nil { return err } } @@ -997,14 +1033,14 @@ func packDir(ar *zip.Writer, repo *repomgr.Repo, dirID, dirPath string, cryptKey return nil } -func packFiles(ar *zip.Writer, dirent *fsmgr.SeafDirent, repo *repomgr.Repo, parentPath string, cryptKey *seafileCrypt) error { +func packFiles(ar *zip.Writer, dirent *fsmgr.SeafDirent, repo *repomgr.Repo, parentPath, baseName string, cryptKey *seafileCrypt) error { file, err := fsmgr.GetSeafile(repo.StoreID, dirent.ID) if err != nil { err := fmt.Errorf("failed to get seafile : %v", err) return err } - filePath := filepath.Join(parentPath, dirent.Name) + filePath := filepath.Join(parentPath, baseName) filePath = strings.TrimLeft(filePath, "/") fileHeader := new(zip.FileHeader) diff --git a/server/pack-dir.c b/server/pack-dir.c index df25d99..47b0435 100644 --- a/server/pack-dir.c +++ b/server/pack-dir.c @@ -12,6 +12,7 @@ #include "seafile-session.h" #include "pack-dir.h" +#include "seaf-utils.h" #include #include @@ -67,6 +68,7 @@ do_iconv (char *fromcode, char *tocode, char *in) static int add_file_to_archive (PackDirData *data, const char *parent_dir, + const char *base_name, SeafDirent *dent) { struct archive *a = data->a; @@ -91,7 +93,7 @@ add_file_to_archive (PackDirData *data, int dec_out_len = -1; int ret = 0; - pathname = g_build_filename (top_dir_name, parent_dir, dent->name, NULL); + pathname = g_build_filename (top_dir_name, parent_dir, base_name, NULL); file = seaf_fs_manager_get_seafile (seaf->fs_mgr, data->store_id, data->repo_version, @@ -332,7 +334,7 @@ archive_dir (PackDirData *data, dent = ptr->data; if (S_ISREG(dent->mode)) { - ret = add_file_to_archive (data, dirpath, dent); + ret = add_file_to_archive (data, dirpath, dent->name, dent); if (ret == 0) { g_atomic_int_inc (&progress->zipped); } @@ -340,7 +342,7 @@ archive_dir (PackDirData *data, if (archive_version_number() >= 3000001) { /* Symlink in zip arhive is not supported in earlier version * of libarchive */ - ret = add_file_to_archive (data, dirpath, dent); + ret = add_file_to_archive (data, dirpath, dent->name, dent); } } else if (S_ISDIR(dent->mode)) { @@ -401,31 +403,80 @@ pack_dir_data_new (const char *store_id, return data; } +static gboolean +name_exists (GList *file_list, const char *filename) +{ + GList *ptr; + char *name; + + for (ptr = file_list; ptr != NULL; ptr = ptr->next) { + name = ptr->data; + if (strcmp (name, filename) == 0) + return TRUE; + } + + return FALSE; +} + +static char * +generate_unique_filename (const char *file, GList *file_list) +{ + int i = 1; + char *name, *ext, *unique_name; + + unique_name = g_strdup(file); + split_filename (unique_name, &name, &ext); + while (name_exists (file_list, unique_name)) { + g_free (unique_name); + if (ext) + unique_name = g_strdup_printf ("%s (%d).%s", name, i, ext); + else + unique_name = g_strdup_printf ("%s (%d)", name, i); + i++; + } + + g_free (name); + g_free (ext); + + return unique_name; +} + static int archive_multi (PackDirData *data, GList *dirent_list, Progress *progress) { GList *iter; SeafDirent *dirent; + GList *file_list = NULL; for (iter = dirent_list; iter; iter = iter->next) { - if (progress->canceled) + char *unique_name = NULL; + if (progress->canceled) { + string_list_free (file_list); return -1; + } dirent = iter->data; if (S_ISREG(dirent->mode)) { - if (add_file_to_archive (data, "", dirent) < 0) { + unique_name = generate_unique_filename (dirent->name, file_list); + file_list = g_list_prepend (file_list, unique_name); + if (add_file_to_archive (data, "", unique_name, dirent) < 0) { + string_list_free (file_list); seaf_warning ("Failed to archive file: %s.\n", dirent->name); return -1; } g_atomic_int_inc (&progress->zipped); } else if (S_ISDIR(dirent->mode)) { - if (archive_dir (data, dirent->id, dirent->name, progress) < 0) { + unique_name = generate_unique_filename (dirent->name, file_list); + file_list = g_list_prepend (file_list, unique_name); + if (archive_dir (data, dirent->id, unique_name, progress) < 0) { + string_list_free (file_list); seaf_warning ("Failed to archive dir: %s.\n", dirent->name); return -1; } } } + string_list_free (file_list); return 0; } diff --git a/server/repo-op.c b/server/repo-op.c index 637fba9..21fd2e8 100644 --- a/server/repo-op.c +++ b/server/repo-op.c @@ -22,6 +22,7 @@ #include "diff-simple.h" #include "merge-new.h" #include "change-set.h" +#include "seaf-utils.h" #include "seaf-db.h" @@ -97,21 +98,6 @@ filename_exists (GList *entries, const char *filename) return FALSE; } -static void -split_filename (const char *filename, char **name, char **ext) -{ - char *dot; - - dot = strrchr (filename, '.'); - if (dot) { - *ext = g_strdup (dot + 1); - *name = g_strndup (filename, dot - filename); - } else { - *name = g_strdup (filename); - *ext = NULL; - } -} - static char * generate_unique_filename (const char *file, GList *entries) {