diff --git a/fileserver/fileop.go b/fileserver/fileop.go index 7dcf39e..e32e783 100644 --- a/fileserver/fileop.go +++ b/fileserver/fileop.go @@ -1126,6 +1126,7 @@ func uploadAPICB(rsp http.ResponseWriter, r *http.Request) *appError { fsm, err := parseUploadHeaders(r) if err != nil { + formatJSONError(rsp, err) return err } @@ -1153,6 +1154,7 @@ func uploadAjaxCB(rsp http.ResponseWriter, r *http.Request) *appError { fsm, err := parseUploadHeaders(r) if err != nil { + formatJSONError(rsp, err) return err } @@ -1671,9 +1673,59 @@ func parseUploadHeaders(r *http.Request) (*recvData, *appError) { parseContentRange(ranges, fsm) } + var contentLen int64 + lenstr := r.Header.Get("Content-Length") + if lenstr != "" { + conLen, _ := strconv.ParseInt(lenstr, 10, 64) + contentLen = conLen + if contentLen < 0 { + contentLen = 0 + } + } + if err := checkQuotaByContentLength(r, repoID, contentLen); err != nil { + return nil, err + } + if err := checkFileSizeByContentLength(r, contentLen); err != nil { + return nil, err + } + return fsm, nil } +// Check whether the file to be uploaded would exceed the quota before receiving the body, in order to avoid unnecessarily receiving the body. +// After receiving the body, the quota is checked again to handle cases where the Content-Length in the request header is missing, which could make the initial quota check inaccurate. +func checkQuotaByContentLength(r *http.Request, repoID string, contentLen int64) *appError { + if r.Method != "PUT" && r.Method != "POST" { + return nil + } + + ret, err := checkQuota(repoID, contentLen) + if err != nil { + msg := "Internal error.\n" + err := fmt.Errorf("failed to check quota: %v", err) + return &appError{err, msg, http.StatusInternalServerError} + } + if ret == 1 { + msg := "Out of quota.\n" + return &appError{nil, msg, seafHTTPResNoQuota} + } + + return nil +} + +func checkFileSizeByContentLength(r *http.Request, contentLen int64) *appError { + if r.Method != "PUT" && r.Method != "POST" { + return nil + } + + if option.MaxUploadSize > 0 && uint64(contentLen) > option.MaxUploadSize { + msg := "File size is too large.\n" + return &appError{nil, msg, seafHTTPResTooLarge} + } + + return nil +} + func postMultiFiles(rsp http.ResponseWriter, r *http.Request, repoID, parentDir, user string, fsm *recvData, replace bool, lastModify int64, isAjax bool) *appError { fileNames := fsm.fileNames @@ -2972,6 +3024,7 @@ func updateAPICB(rsp http.ResponseWriter, r *http.Request) *appError { fsm, err := parseUploadHeaders(r) if err != nil { + formatJSONError(rsp, err) return err } @@ -2992,6 +3045,7 @@ func updateAjaxCB(rsp http.ResponseWriter, r *http.Request) *appError { fsm, err := parseUploadHeaders(r) if err != nil { + formatJSONError(rsp, err) return err } @@ -3323,6 +3377,7 @@ func checkFileExists(storeID, rootID, parentDir, fileName string) (bool, error) func uploadBlksAPICB(rsp http.ResponseWriter, r *http.Request) *appError { fsm, err := parseUploadHeaders(r) if err != nil { + formatJSONError(rsp, err) return err } @@ -3560,6 +3615,7 @@ func indexExistedFileBlocks(repoID string, version int, blkIDs []string, fileSiz func uploadRawBlksAPICB(rsp http.ResponseWriter, r *http.Request) *appError { fsm, err := parseUploadHeaders(r) if err != nil { + formatJSONError(rsp, err) return err } diff --git a/server/seafile-session.c b/server/seafile-session.c index dd9d64f..910e91f 100644 --- a/server/seafile-session.c +++ b/server/seafile-session.c @@ -39,6 +39,7 @@ load_fileserver_config (SeafileSession *session) int max_index_processing_threads; int fixed_block_size_mb; int max_indexing_threads; + gint64 max_upload_size; web_token_expire_time = g_key_file_get_integer (session->config, "fileserver", "web_token_expire_time", @@ -89,6 +90,19 @@ load_fileserver_config (SeafileSession *session) seaf_message ("fileserver: max_indexing_threads = %d\n", session->max_indexing_threads); + GError *err = NULL; + max_upload_size = g_key_file_get_int64(session->config, "fileserver", "max_upload_size", &err); + if (err) { + max_upload_size = -1; + g_clear_error(&err); + } else if (max_upload_size > 0) { + max_upload_size = max_upload_size * 1000000; + } + session->max_upload_size = max_upload_size; + + seaf_message ("fileserver: max_upload_size = %d\n", + session->max_upload_size); + return; } diff --git a/server/seafile-session.h b/server/seafile-session.h index c72a4ba..9137ce2 100644 --- a/server/seafile-session.h +++ b/server/seafile-session.h @@ -91,6 +91,7 @@ struct _SeafileSession { int max_index_processing_threads; gint64 fixed_block_size; int max_indexing_threads; + gint64 max_upload_size; // For notification server NotifManager *notif_mgr; diff --git a/server/upload-file.c b/server/upload-file.c index 5012a57..9e92412 100755 --- a/server/upload-file.c +++ b/server/upload-file.c @@ -224,7 +224,6 @@ check_tmp_file_list (GList *tmp_files, int *error_code) char *tmp_file; SeafStat st; gint64 total_size = 0; - gint64 max_upload_size; for (ptr = tmp_files; ptr; ptr = ptr->next) { tmp_file = ptr->data; @@ -237,15 +236,8 @@ check_tmp_file_list (GList *tmp_files, int *error_code) total_size += (gint64)st.st_size; } - /* default is MB */ - max_upload_size = seaf_cfg_manager_get_config_int64 (seaf->cfg_mgr, "fileserver", - "max_upload_size"); - if (max_upload_size > 0) - max_upload_size = max_upload_size * 1000000; - else - max_upload_size = -1; - if (max_upload_size > 0 && total_size > max_upload_size) { + if (seaf->max_upload_size > 0 && total_size > seaf->max_upload_size) { seaf_debug ("[upload] File size is too large.\n"); *error_code = ERROR_SIZE; return FALSE; @@ -2554,8 +2546,9 @@ upload_headers_cb (evhtp_request_t *req, evhtp_headers_t *hdr, void *arg) RecvFSM *fsm = NULL; Progress *progress = NULL; int error_code = EVHTP_RES_BADREQ; + htp_method method = evhtp_request_get_method(req); - if (evhtp_request_get_method(req) == htp_method_OPTIONS) { + if (method == htp_method_OPTIONS) { return EVHTP_RES_OK; } @@ -2579,6 +2572,25 @@ upload_headers_cb (evhtp_request_t *req, evhtp_headers_t *hdr, void *arg) goto err; } + if (method == htp_method_POST || method == htp_method_PUT) { + gint64 content_len = get_content_length (req); + // Check whether the file to be uploaded would exceed the quota before receiving the body, in order to avoid unnecessarily receiving the body. + // After receiving the body, the quota is checked again to handle cases where the Content-Length in the request header is missing, which could make the initial quota check inaccurate. + if (seaf_quota_manager_check_quota_with_delta (seaf->quota_mgr, + repo_id, + content_len) != 0) { + error_code = SEAF_HTTP_RES_NOQUOTA; + err_msg = "Out of quota.\n"; + goto err; + } + + if (seaf->max_upload_size > 0 && content_len > seaf->max_upload_size) { + error_code = ERROR_SIZE; + err_msg = "File size is too large.\n"; + goto err; + } + } + boundary = get_boundary (hdr); if (!boundary) { err_msg = "Wrong boundary in url";