mirror of
				https://github.com/go-gitea/gitea
				synced 2025-10-31 11:28:24 +00:00 
			
		
		
		
	Fix storage path logic especially for relative paths (#26441)
This PR rewrites the function `getStorage` and make it more clear. Include tests from #26435, thanks @earl-warren --------- Co-authored-by: Earl Warren <contact@earl-warren.org>
This commit is contained in:
		| @@ -91,134 +91,172 @@ func getStorage(rootCfg ConfigProvider, name, typ string, sec ConfigSection) (*S | ||||
| 		return nil, errors.New("no name for storage") | ||||
| 	} | ||||
|  | ||||
| 	var targetSec ConfigSection | ||||
| 	// check typ first | ||||
| 	if typ != "" { | ||||
| 		var err error | ||||
| 		targetSec, err = rootCfg.GetSection(storageSectionName + "." + typ) | ||||
| 		if err != nil { | ||||
| 			if !IsValidStorageType(StorageType(typ)) { | ||||
| 				return nil, fmt.Errorf("get section via storage type %q failed: %v", typ, err) | ||||
| 			} | ||||
| 		} | ||||
| 		if targetSec != nil { | ||||
| 			targetType := targetSec.Key("STORAGE_TYPE").String() | ||||
| 			if targetType == "" { | ||||
| 				if !IsValidStorageType(StorageType(typ)) { | ||||
| 					return nil, fmt.Errorf("unknow storage type %q", typ) | ||||
| 				} | ||||
| 				targetSec.Key("STORAGE_TYPE").SetValue(typ) | ||||
| 			} else if !IsValidStorageType(StorageType(targetType)) { | ||||
| 				return nil, fmt.Errorf("unknow storage type %q for section storage.%v", targetType, typ) | ||||
| 			} | ||||
| 		} | ||||
| 	targetSec, tp, err := getStorageTargetSection(rootCfg, name, typ, sec) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	if targetSec == nil && sec != nil { | ||||
| 		secTyp := sec.Key("STORAGE_TYPE").String() | ||||
| 		if IsValidStorageType(StorageType(secTyp)) { | ||||
| 			targetSec = sec | ||||
| 		} else if secTyp != "" { | ||||
| 			targetSec, _ = rootCfg.GetSection(storageSectionName + "." + secTyp) | ||||
| 		} | ||||
| 	} | ||||
| 	overrideSec := getStorageOverrideSection(rootCfg, targetSec, sec, tp, name) | ||||
|  | ||||
| 	targetSecIsStoragename := false | ||||
| 	storageNameSec, _ := rootCfg.GetSection(storageSectionName + "." + name) | ||||
| 	if targetSec == nil { | ||||
| 		targetSec = storageNameSec | ||||
| 		targetSecIsStoragename = storageNameSec != nil | ||||
| 	targetType := targetSec.Key("STORAGE_TYPE").String() | ||||
| 	switch targetType { | ||||
| 	case string(LocalStorageType): | ||||
| 		return getStorageForLocal(targetSec, overrideSec, tp, name) | ||||
| 	case string(MinioStorageType): | ||||
| 		return getStorageForMinio(targetSec, overrideSec, tp, name) | ||||
| 	default: | ||||
| 		return nil, fmt.Errorf("unsupported storage type %q", targetType) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| 	if targetSec == nil { | ||||
| 		targetSec = getDefaultStorageSection(rootCfg) | ||||
| 	} else { | ||||
| 		targetType := targetSec.Key("STORAGE_TYPE").String() | ||||
| 		switch { | ||||
| 		case targetType == "": | ||||
| 			if targetSec != storageNameSec && storageNameSec != nil { | ||||
| 				targetSec = storageNameSec | ||||
| 				targetSecIsStoragename = true | ||||
| 				if targetSec.Key("STORAGE_TYPE").String() == "" { | ||||
| 					return nil, fmt.Errorf("storage section %s.%s has no STORAGE_TYPE", storageSectionName, name) | ||||
| 				} | ||||
| 			} else { | ||||
| 				if targetSec.Key("PATH").String() == "" { | ||||
| 					targetSec = getDefaultStorageSection(rootCfg) | ||||
| 				} else { | ||||
| 					targetSec.Key("STORAGE_TYPE").SetValue("local") | ||||
| 				} | ||||
| 			} | ||||
| 		default: | ||||
| 			newTargetSec, _ := rootCfg.GetSection(storageSectionName + "." + targetType) | ||||
| 			if newTargetSec == nil { | ||||
| 				if !IsValidStorageType(StorageType(targetType)) { | ||||
| 					return nil, fmt.Errorf("invalid storage section %s.%q", storageSectionName, targetType) | ||||
| 				} | ||||
| 			} else { | ||||
| 				targetSec = newTargetSec | ||||
| 				if IsValidStorageType(StorageType(targetType)) { | ||||
| 					tp := targetSec.Key("STORAGE_TYPE").String() | ||||
| 					if tp == "" { | ||||
| 						targetSec.Key("STORAGE_TYPE").SetValue(targetType) | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| type targetSecType int | ||||
|  | ||||
| const ( | ||||
| 	targetSecIsTyp             targetSecType = iota // target section is [storage.type] which the type from parameter | ||||
| 	targetSecIsStorage                              // target section is [storage] | ||||
| 	targetSecIsDefault                              // target section is the default value | ||||
| 	targetSecIsStorageWithName                      // target section is [storage.name] | ||||
| 	targetSecIsSec                                  // target section is from the name seciont [name] | ||||
| ) | ||||
|  | ||||
| func getStorageSectionByType(rootCfg ConfigProvider, typ string) (ConfigSection, targetSecType, error) { | ||||
| 	targetSec, err := rootCfg.GetSection(storageSectionName + "." + typ) | ||||
| 	if err != nil { | ||||
| 		if !IsValidStorageType(StorageType(typ)) { | ||||
| 			return nil, 0, fmt.Errorf("get section via storage type %q failed: %v", typ, err) | ||||
| 		} | ||||
| 		// if typ is a valid storage type, but there is no [storage.local] or [storage.minio] section | ||||
| 		// it's not an error | ||||
| 		return nil, 0, nil | ||||
| 	} | ||||
|  | ||||
| 	targetType := targetSec.Key("STORAGE_TYPE").String() | ||||
| 	if !IsValidStorageType(StorageType(targetType)) { | ||||
| 		return nil, fmt.Errorf("invalid storage type %q", targetType) | ||||
| 	if targetType == "" { | ||||
| 		if !IsValidStorageType(StorageType(typ)) { | ||||
| 			return nil, 0, fmt.Errorf("unknow storage type %q", typ) | ||||
| 		} | ||||
| 		targetSec.Key("STORAGE_TYPE").SetValue(typ) | ||||
| 	} else if !IsValidStorageType(StorageType(targetType)) { | ||||
| 		return nil, 0, fmt.Errorf("unknow storage type %q for section storage.%v", targetType, typ) | ||||
| 	} | ||||
|  | ||||
| 	// extra config section will be read SERVE_DIRECT, PATH, MINIO_BASE_PATH, MINIO_BUCKET to override the targetsec when possible | ||||
| 	extraConfigSec := sec | ||||
| 	if extraConfigSec == nil { | ||||
| 		extraConfigSec = storageNameSec | ||||
| 	return targetSec, targetSecIsTyp, nil | ||||
| } | ||||
|  | ||||
| func getStorageTargetSection(rootCfg ConfigProvider, name, typ string, sec ConfigSection) (ConfigSection, targetSecType, error) { | ||||
| 	// check typ first | ||||
| 	if typ == "" { | ||||
| 		if sec != nil { // check sec's type secondly | ||||
| 			typ = sec.Key("STORAGE_TYPE").String() | ||||
| 			if IsValidStorageType(StorageType(typ)) { | ||||
| 				if targetSec, _ := rootCfg.GetSection(storageSectionName + "." + typ); targetSec == nil { | ||||
| 					return sec, targetSecIsSec, nil | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	var storage Storage | ||||
| 	storage.Type = StorageType(targetType) | ||||
|  | ||||
| 	switch targetType { | ||||
| 	case string(LocalStorageType): | ||||
| 		targetPath := ConfigSectionKeyString(targetSec, "PATH", "") | ||||
| 		if targetPath == "" { | ||||
| 			targetPath = AppDataPath | ||||
| 		} else if !filepath.IsAbs(targetPath) { | ||||
| 			targetPath = filepath.Join(AppDataPath, targetPath) | ||||
| 	if typ != "" { | ||||
| 		targetSec, tp, err := getStorageSectionByType(rootCfg, typ) | ||||
| 		if targetSec != nil || err != nil { | ||||
| 			return targetSec, tp, err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 		var fallbackPath string | ||||
| 		if targetSecIsStoragename { | ||||
| 			fallbackPath = targetPath | ||||
| 		} else { | ||||
| 			fallbackPath = filepath.Join(targetPath, name) | ||||
| 		} | ||||
| 	// check stoarge name thirdly | ||||
| 	targetSec, _ := rootCfg.GetSection(storageSectionName + "." + name) | ||||
| 	if targetSec != nil { | ||||
| 		targetType := targetSec.Key("STORAGE_TYPE").String() | ||||
| 		switch { | ||||
| 		case targetType == "": | ||||
| 			if targetSec.Key("PATH").String() == "" { // both storage type and path are empty, use default | ||||
| 				return getDefaultStorageSection(rootCfg), targetSecIsDefault, nil | ||||
| 			} | ||||
|  | ||||
| 		if extraConfigSec == nil { | ||||
| 			storage.Path = fallbackPath | ||||
| 		} else { | ||||
| 			storage.Path = ConfigSectionKeyString(extraConfigSec, "PATH", fallbackPath) | ||||
| 			if !filepath.IsAbs(storage.Path) { | ||||
| 				storage.Path = filepath.Join(targetPath, storage.Path) | ||||
| 			targetSec.Key("STORAGE_TYPE").SetValue("local") | ||||
| 		default: | ||||
| 			targetSec, tp, err := getStorageSectionByType(rootCfg, targetType) | ||||
| 			if targetSec != nil || err != nil { | ||||
| 				return targetSec, tp, err | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 	case string(MinioStorageType): | ||||
| 		if err := targetSec.MapTo(&storage.MinioConfig); err != nil { | ||||
| 			return nil, fmt.Errorf("map minio config failed: %v", err) | ||||
| 		return targetSec, targetSecIsStorageWithName, nil | ||||
| 	} | ||||
|  | ||||
| 	return getDefaultStorageSection(rootCfg), targetSecIsDefault, nil | ||||
| } | ||||
|  | ||||
| // getStorageOverrideSection override section will be read SERVE_DIRECT, PATH, MINIO_BASE_PATH, MINIO_BUCKET to override the targetsec when possible | ||||
| func getStorageOverrideSection(rootConfig ConfigProvider, targetSec, sec ConfigSection, targetSecType targetSecType, name string) ConfigSection { | ||||
| 	if targetSecType == targetSecIsSec { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	if sec != nil { | ||||
| 		return sec | ||||
| 	} | ||||
|  | ||||
| 	if targetSecType != targetSecIsStorageWithName { | ||||
| 		nameSec, _ := rootConfig.GetSection(storageSectionName + "." + name) | ||||
| 		return nameSec | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func getStorageForLocal(targetSec, overrideSec ConfigSection, tp targetSecType, name string) (*Storage, error) { | ||||
| 	storage := Storage{ | ||||
| 		Type: StorageType(targetSec.Key("STORAGE_TYPE").String()), | ||||
| 	} | ||||
|  | ||||
| 	targetPath := ConfigSectionKeyString(targetSec, "PATH", "") | ||||
| 	var fallbackPath string | ||||
| 	if targetPath == "" { // no path | ||||
| 		fallbackPath = filepath.Join(AppDataPath, name) | ||||
| 	} else { | ||||
| 		if tp == targetSecIsStorage || tp == targetSecIsDefault { | ||||
| 			fallbackPath = filepath.Join(targetPath, name) | ||||
| 		} else { | ||||
| 			fallbackPath = targetPath | ||||
| 		} | ||||
| 		if !filepath.IsAbs(fallbackPath) { | ||||
| 			fallbackPath = filepath.Join(AppDataPath, fallbackPath) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 		storage.MinioConfig.BasePath = name + "/" | ||||
|  | ||||
| 		if extraConfigSec != nil { | ||||
| 			storage.MinioConfig.ServeDirect = ConfigSectionKeyBool(extraConfigSec, "SERVE_DIRECT", storage.MinioConfig.ServeDirect) | ||||
| 			storage.MinioConfig.BasePath = ConfigSectionKeyString(extraConfigSec, "MINIO_BASE_PATH", storage.MinioConfig.BasePath) | ||||
| 			storage.MinioConfig.Bucket = ConfigSectionKeyString(extraConfigSec, "MINIO_BUCKET", storage.MinioConfig.Bucket) | ||||
| 	if overrideSec == nil { // no override section | ||||
| 		storage.Path = fallbackPath | ||||
| 	} else { | ||||
| 		storage.Path = ConfigSectionKeyString(overrideSec, "PATH", "") | ||||
| 		if storage.Path == "" { // overrideSec has no path | ||||
| 			storage.Path = fallbackPath | ||||
| 		} else if !filepath.IsAbs(storage.Path) { | ||||
| 			if targetPath == "" { | ||||
| 				storage.Path = filepath.Join(AppDataPath, storage.Path) | ||||
| 			} else { | ||||
| 				storage.Path = filepath.Join(targetPath, storage.Path) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return &storage, nil | ||||
| } | ||||
|  | ||||
| func getStorageForMinio(targetSec, overrideSec ConfigSection, tp targetSecType, name string) (*Storage, error) { | ||||
| 	var storage Storage | ||||
| 	storage.Type = StorageType(targetSec.Key("STORAGE_TYPE").String()) | ||||
| 	if err := targetSec.MapTo(&storage.MinioConfig); err != nil { | ||||
| 		return nil, fmt.Errorf("map minio config failed: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	if storage.MinioConfig.BasePath == "" { | ||||
| 		storage.MinioConfig.BasePath = name + "/" | ||||
| 	} | ||||
|  | ||||
| 	if overrideSec != nil { | ||||
| 		storage.MinioConfig.ServeDirect = ConfigSectionKeyBool(overrideSec, "SERVE_DIRECT", storage.MinioConfig.ServeDirect) | ||||
| 		storage.MinioConfig.BasePath = ConfigSectionKeyString(overrideSec, "MINIO_BASE_PATH", storage.MinioConfig.BasePath) | ||||
| 		storage.MinioConfig.Bucket = ConfigSectionKeyString(overrideSec, "MINIO_BUCKET", storage.MinioConfig.Bucket) | ||||
| 	} | ||||
| 	return &storage, nil | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user