| 1562 | } |
| 1563 | |
| 1564 | func (f *sendReceiveFolder) pullBlock(ctx context.Context, state pullBlockState, out chan<- *sharedPullerState) { |
| 1565 | // Get an fd to the temporary file. Technically we don't need it until |
| 1566 | // after fetching the block, but if we run into an error here there is |
| 1567 | // no point in issuing the request to the network. |
| 1568 | fd, err := state.tempFile() |
| 1569 | if err != nil { |
| 1570 | out <- state.sharedPullerState |
| 1571 | return |
| 1572 | } |
| 1573 | |
| 1574 | if state.block.IsEmpty() { |
| 1575 | // There is no need to request a block of all zeroes. Pretend we |
| 1576 | // requested it and handled it correctly. |
| 1577 | if state.reused != 0 || f.DisableSparseFiles { |
| 1578 | // We are reusing a file (contents apparently weren't all-zeroes |
| 1579 | // previously), or sparse files are disabled, so we need to |
| 1580 | // actually write the block. |
| 1581 | zeroes := make([]byte, state.block.Size) |
| 1582 | err = f.limitedWriteAt(ctx, fd, zeroes, state.block.Offset) |
| 1583 | } |
| 1584 | if err != nil { |
| 1585 | state.fail(fmt.Errorf("save: %w", err)) |
| 1586 | } else { |
| 1587 | state.pullDone(state.block) |
| 1588 | } |
| 1589 | out <- state.sharedPullerState |
| 1590 | return |
| 1591 | } |
| 1592 | |
| 1593 | var lastError error |
| 1594 | candidates := f.model.blockAvailability(f.FolderConfiguration, state.file, state.block) |
| 1595 | loop: |
| 1596 | for { |
| 1597 | select { |
| 1598 | case <-ctx.Done(): |
| 1599 | state.fail(fmt.Errorf("folder stopped: %w", ctx.Err())) |
| 1600 | break loop |
| 1601 | default: |
| 1602 | } |
| 1603 | |
| 1604 | // Select the least busy device to pull the block from. If we found no |
| 1605 | // feasible device at all, fail the block (and in the long run, the |
| 1606 | // file). |
| 1607 | found := activity.leastBusy(candidates) |
| 1608 | if found == -1 { |
| 1609 | if lastError != nil { |
| 1610 | state.fail(fmt.Errorf("pull: %w", lastError)) |
| 1611 | } else { |
| 1612 | state.fail(fmt.Errorf("pull: %w", errNoDevice)) |
| 1613 | } |
| 1614 | break |
| 1615 | } |
| 1616 | |
| 1617 | selected := candidates[found] |
| 1618 | candidates[found] = candidates[len(candidates)-1] |
| 1619 | candidates = candidates[:len(candidates)-1] |
| 1620 | |
| 1621 | // Fetch the block, while marking the selected device as in use so that |