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:
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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;
|
||||||
|
@@ -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";
|
||||||
|
Reference in New Issue
Block a user