mirror of
https://github.com/haiwen/seafile-server.git
synced 2025-09-22 03:28:48 +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:
@@ -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
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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";
|
||||
|
Reference in New Issue
Block a user