public async Task <ActionResult <DebugSymbolUploadResult> > StartUpload( [Required][FromBody] DebugSymbolUploadRequest request) { if (request.SymbolPath.Contains('\\')) { return(BadRequest("The path contains a Windows line separator")); } if (request.SymbolPath.StartsWith("/") || request.SymbolPath.Contains("..")) { return(BadRequest("The path must not start with a slash or contain two dots in a row")); } logger.LogInformation("Upload request for symbol: {SymbolPath}", request.SymbolPath); if (request.SymbolPath.Count(c => c == '/') < 2 || !request.SymbolPath.EndsWith(".sym")) { return(BadRequest("The path must contain at least two path separators and end in .sym")); } if (!remoteStorage.Configured) { throw new HttpResponseException() { Status = StatusCodes.Status500InternalServerError, Value = "Remote storage is not configured", }; } var folder = await StorageItem.GetSymbolsFolder(database); if (folder == null) { throw new HttpResponseException() { Status = StatusCodes.Status500InternalServerError, Value = "Storage folder is missing", }; } var user = HttpContext.AuthenticatedUser() !; var symbol = new DebugSymbol() { // Start in non-active state until the upload is ready Active = false, Uploaded = false, Name = request.SymbolPath.Split("/").Last(), RelativePath = request.SymbolPath, CreatedById = user.Id, Size = request.Size, }; var storageItem = new StorageItem() { Name = symbol.StorageFileName, Parent = folder, Ftype = FileType.File, Special = true, ReadAccess = FileAccess.Developer, WriteAccess = FileAccess.Nobody, }; symbol.StoredInItem = storageItem; await database.StorageItems.AddAsync(storageItem); await database.DebugSymbols.AddAsync(symbol); // This save will fail if duplicate upload was attempted await database.SaveChangesAsync(); jobClient.Enqueue <CountFolderItemsJob>((x) => x.Execute(folder.Id, CancellationToken.None)); logger.LogInformation("New DebugSymbol ({Id}) \"{RelativePath}\" created by {Email}", symbol.Id, symbol.RelativePath, user.Email); // Create a version to upload to var version = await symbol.StoredInItem.CreateNextVersion(database); var file = await version.CreateStorageFile(database, DateTime.UtcNow + AppInfo.RemoteStorageUploadExpireTime, request.Size); if (request.Size != file.Size) { throw new Exception("Logic error in StorageFile size setting"); } await database.SaveChangesAsync(); logger.LogInformation("Upload of DebugSymbol {Id} starting from {RemoteIpAddress}", symbol.Id, HttpContext.Connection.RemoteIpAddress); jobClient.Schedule <DeleteDebugSymbolIfUploadFailedJob>(x => x.Execute(symbol.Id, CancellationToken.None), AppInfo.RemoteStorageUploadExpireTime * 2); return(new DebugSymbolUploadResult() { UploadUrl = remoteStorage.CreatePresignedUploadURL(file.UploadPath, AppInfo.RemoteStorageUploadExpireTime), VerifyToken = new StorageUploadVerifyToken(dataProtector, file.UploadPath, file.StoragePath, file.Size.Value, file.Id, symbol.Id, null, null).ToString() }); }