1
0
mirror of https://github.com/haiwen/seafile-server.git synced 2025-09-22 11:39:05 +00:00

Check quota before recv body (#773)

* Check quota before recv body

* Check max upload file size before recv body

---------

Co-authored-by: Heran Yang <heran.yang@seafile.com>
This commit is contained in:
feiniks
2025-09-12 17:48:43 +08:00
committed by GitHub
parent 6a7c1df693
commit a1ba8d7db7
4 changed files with 93 additions and 10 deletions

View File

@@ -1126,6 +1126,7 @@ func uploadAPICB(rsp http.ResponseWriter, r *http.Request) *appError {
fsm, err := parseUploadHeaders(r) fsm, err := parseUploadHeaders(r)
if err != nil { if err != nil {
formatJSONError(rsp, err)
return err return err
} }
@@ -1153,6 +1154,7 @@ func uploadAjaxCB(rsp http.ResponseWriter, r *http.Request) *appError {
fsm, err := parseUploadHeaders(r) fsm, err := parseUploadHeaders(r)
if err != nil { if err != nil {
formatJSONError(rsp, err)
return err return err
} }
@@ -1671,9 +1673,59 @@ func parseUploadHeaders(r *http.Request) (*recvData, *appError) {
parseContentRange(ranges, fsm) 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 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 { func postMultiFiles(rsp http.ResponseWriter, r *http.Request, repoID, parentDir, user string, fsm *recvData, replace bool, lastModify int64, isAjax bool) *appError {
fileNames := fsm.fileNames fileNames := fsm.fileNames
@@ -2972,6 +3024,7 @@ func updateAPICB(rsp http.ResponseWriter, r *http.Request) *appError {
fsm, err := parseUploadHeaders(r) fsm, err := parseUploadHeaders(r)
if err != nil { if err != nil {
formatJSONError(rsp, err)
return err return err
} }
@@ -2992,6 +3045,7 @@ func updateAjaxCB(rsp http.ResponseWriter, r *http.Request) *appError {
fsm, err := parseUploadHeaders(r) fsm, err := parseUploadHeaders(r)
if err != nil { if err != nil {
formatJSONError(rsp, err)
return 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 { func uploadBlksAPICB(rsp http.ResponseWriter, r *http.Request) *appError {
fsm, err := parseUploadHeaders(r) fsm, err := parseUploadHeaders(r)
if err != nil { if err != nil {
formatJSONError(rsp, err)
return 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 { func uploadRawBlksAPICB(rsp http.ResponseWriter, r *http.Request) *appError {
fsm, err := parseUploadHeaders(r) fsm, err := parseUploadHeaders(r)
if err != nil { if err != nil {
formatJSONError(rsp, err)
return err return err
} }

View File

@@ -39,6 +39,7 @@ load_fileserver_config (SeafileSession *session)
int max_index_processing_threads; int max_index_processing_threads;
int fixed_block_size_mb; int fixed_block_size_mb;
int max_indexing_threads; int max_indexing_threads;
gint64 max_upload_size;
web_token_expire_time = g_key_file_get_integer (session->config, web_token_expire_time = g_key_file_get_integer (session->config,
"fileserver", "web_token_expire_time", "fileserver", "web_token_expire_time",
@@ -89,6 +90,19 @@ load_fileserver_config (SeafileSession *session)
seaf_message ("fileserver: max_indexing_threads = %d\n", seaf_message ("fileserver: max_indexing_threads = %d\n",
session->max_indexing_threads); 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; return;
} }

View File

@@ -91,6 +91,7 @@ struct _SeafileSession {
int max_index_processing_threads; int max_index_processing_threads;
gint64 fixed_block_size; gint64 fixed_block_size;
int max_indexing_threads; int max_indexing_threads;
gint64 max_upload_size;
// For notification server // For notification server
NotifManager *notif_mgr; NotifManager *notif_mgr;

View File

@@ -224,7 +224,6 @@ check_tmp_file_list (GList *tmp_files, int *error_code)
char *tmp_file; char *tmp_file;
SeafStat st; SeafStat st;
gint64 total_size = 0; gint64 total_size = 0;
gint64 max_upload_size;
for (ptr = tmp_files; ptr; ptr = ptr->next) { for (ptr = tmp_files; ptr; ptr = ptr->next) {
tmp_file = ptr->data; tmp_file = ptr->data;
@@ -237,15 +236,8 @@ check_tmp_file_list (GList *tmp_files, int *error_code)
total_size += (gint64)st.st_size; 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"); seaf_debug ("[upload] File size is too large.\n");
*error_code = ERROR_SIZE; *error_code = ERROR_SIZE;
return FALSE; return FALSE;
@@ -2554,8 +2546,9 @@ upload_headers_cb (evhtp_request_t *req, evhtp_headers_t *hdr, void *arg)
RecvFSM *fsm = NULL; RecvFSM *fsm = NULL;
Progress *progress = NULL; Progress *progress = NULL;
int error_code = EVHTP_RES_BADREQ; 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; return EVHTP_RES_OK;
} }
@@ -2579,6 +2572,25 @@ upload_headers_cb (evhtp_request_t *req, evhtp_headers_t *hdr, void *arg)
goto err; 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); boundary = get_boundary (hdr);
if (!boundary) { if (!boundary) {
err_msg = "Wrong boundary in url"; err_msg = "Wrong boundary in url";