handleSymlink creates or updates the given symlink
(file protocol.FileInfo, dbUpdateChan chan<- dbUpdateJob, scanChan chan<- string)
| 713 | |
| 714 | // handleSymlink creates or updates the given symlink |
| 715 | func (f *sendReceiveFolder) handleSymlink(file protocol.FileInfo, dbUpdateChan chan<- dbUpdateJob, scanChan chan<- string) { |
| 716 | // Used in the defer closure below, updated by the function body. Take |
| 717 | // care not declare another err. |
| 718 | var err error |
| 719 | |
| 720 | f.evLogger.Log(events.ItemStarted, map[string]string{ |
| 721 | "folder": f.folderID, |
| 722 | "item": file.Name, |
| 723 | "type": "symlink", |
| 724 | "action": "update", |
| 725 | }) |
| 726 | |
| 727 | defer func() { |
| 728 | if err != nil { |
| 729 | slog.Warn("Failed to handle symlink", f.LogAttr(), file.LogAttr(), slogutil.Error(err)) |
| 730 | } else { |
| 731 | slog.Info("Created or updated symlink", f.LogAttr(), file.LogAttr()) |
| 732 | } |
| 733 | f.evLogger.Log(events.ItemFinished, map[string]interface{}{ |
| 734 | "folder": f.folderID, |
| 735 | "item": file.Name, |
| 736 | "error": events.Error(err), |
| 737 | "type": "symlink", |
| 738 | "action": "update", |
| 739 | }) |
| 740 | }() |
| 741 | |
| 742 | f.sl.Debug("Need symlink", slogutil.FilePath(file.Name), slog.Any("cur", slogutil.Expensive(func() any { |
| 743 | curFile, _, _ := f.model.sdb.GetDeviceFile(f.folderID, protocol.LocalDeviceID, file.Name) |
| 744 | return curFile |
| 745 | }))) |
| 746 | |
| 747 | if len(file.SymlinkTarget) == 0 { |
| 748 | // Index entry from a Syncthing predating the support for including |
| 749 | // the link target in the index entry. We log this as an error. |
| 750 | f.newPullError(file.Name, errIncompatibleSymlink) |
| 751 | return |
| 752 | } |
| 753 | |
| 754 | if err = f.handleSymlinkCheckExisting(file, scanChan); err != nil { |
| 755 | f.newPullError(file.Name, fmt.Errorf("handling symlink: %w", err)) |
| 756 | return |
| 757 | } |
| 758 | |
| 759 | // We declare a function that acts on only the path name, so |
| 760 | // we can pass it to InWritableDir. |
| 761 | createLink := func(path string) error { |
| 762 | if err := f.mtimefs.CreateSymlink(string(file.SymlinkTarget), path); err != nil { |
| 763 | return err |
| 764 | } |
| 765 | return f.setPlatformData(&file, path) |
| 766 | } |
| 767 | |
| 768 | if err = f.inWritableDir(createLink, file.Name); err == nil { |
| 769 | dbUpdateChan <- dbUpdateJob{file, dbUpdateHandleSymlink} |
| 770 | } else { |
| 771 | f.newPullError(file.Name, fmt.Errorf("symlink create: %w", err)) |
| 772 | } |