@@ -617,6 +617,84 @@ max_pipe_buffer(int infd)
617
617
#endif
618
618
}
619
619
620
+ #if __linux__
621
+ struct send_worker_ctx {
622
+ int from ; /* read end of pipe, with send data; closed on exit */
623
+ int to ; /* original arbitrary output fd; mustn't be a pipe */
624
+ };
625
+
626
+ static void *
627
+ send_worker (void * arg )
628
+ {
629
+ struct send_worker_ctx * ctx = arg ;
630
+ unsigned int bufsiz = max_pipe_buffer (ctx -> from );
631
+ ssize_t rd ;
632
+
633
+ while ((rd = splice (ctx -> from , NULL , ctx -> to , NULL , bufsiz ,
634
+ SPLICE_F_MOVE | SPLICE_F_MORE )) > 0 )
635
+ ;
636
+
637
+ int err = (rd == -1 ) ? errno : 0 ;
638
+ close (ctx -> from );
639
+ return ((void * )(uintptr_t )err );
640
+ }
641
+ #endif
642
+
643
+ /*
644
+ * Since Linux 5.10, 4d03e3cc59828c82ee89ea6e27a2f3cdf95aaadf
645
+ * ("fs: don't allow kernel reads and writes without iter ops"),
646
+ * ZFS_IOC_SEND* will EINVAL when writing to /dev/null, /dev/zero, &c.
647
+ *
648
+ * This wrapper transparently executes func() with a pipe
649
+ * by spawning a thread to copy from that pipe to the original output
650
+ * in the background.
651
+ *
652
+ * Returns the error from func(), if nonzero,
653
+ * otherwise the error from the thread.
654
+ *
655
+ * No-op if orig_fd is -1, already a pipe, and on not-Linux;
656
+ * as such, it is safe to wrap/call wrapped functions in a wrapped context.
657
+ */
658
+ int
659
+ lzc_send_wrapper (int (* func )(int , void * ), int orig_fd , void * data )
660
+ {
661
+ #if __linux__
662
+ struct stat sb ;
663
+ if (orig_fd != -1 && fstat (orig_fd , & sb ) == -1 )
664
+ return (errno );
665
+ if (orig_fd == -1 || S_ISFIFO (sb .st_mode ))
666
+ return (func (orig_fd , data ));
667
+ if ((fcntl (orig_fd , F_GETFL ) & O_ACCMODE ) == O_RDONLY )
668
+ return (errno = EBADF );
669
+
670
+ int rw [2 ];
671
+ if (pipe2 (rw , O_CLOEXEC ) == -1 )
672
+ return (errno );
673
+
674
+ int err ;
675
+ pthread_t send_thread ;
676
+ struct send_worker_ctx ctx = {.from = rw [0 ], .to = orig_fd };
677
+ if ((err = pthread_create (& send_thread , NULL , send_worker , & ctx ))
678
+ != 0 ) {
679
+ close (rw [0 ]);
680
+ close (rw [1 ]);
681
+ return (errno = err );
682
+ }
683
+
684
+ err = func (rw [1 ], data );
685
+
686
+ void * send_err ;
687
+ close (rw [1 ]);
688
+ pthread_join (send_thread , & send_err );
689
+ if (err == 0 && send_err != 0 )
690
+ errno = err = (uintptr_t )send_err ;
691
+
692
+ return (err );
693
+ #else
694
+ return (func (orig_fd , data ));
695
+ #endif
696
+ }
697
+
620
698
/*
621
699
* Generate a zfs send stream for the specified snapshot and write it to
622
700
* the specified file descriptor.
@@ -687,9 +765,11 @@ lzc_send_resume(const char *snapname, const char *from, int fd,
687
765
* redactnv: nvlist of string -> boolean(ignored) containing the names of all
688
766
* the snapshots that we should redact with respect to.
689
767
* redactbook: Name of the redaction bookmark to create.
768
+ *
769
+ * Pre-wrapped.
690
770
*/
691
- int
692
- lzc_send_resume_redacted (const char * snapname , const char * from , int fd ,
771
+ static int
772
+ lzc_send_resume_redacted_cb_impl (const char * snapname , const char * from , int fd ,
693
773
enum lzc_send_flags flags , uint64_t resumeobj , uint64_t resumeoff ,
694
774
const char * redactbook )
695
775
{
@@ -722,6 +802,40 @@ lzc_send_resume_redacted(const char *snapname, const char *from, int fd,
722
802
return (err );
723
803
}
724
804
805
+ struct lzc_send_resume_redacted {
806
+ const char * snapname ;
807
+ const char * from ;
808
+ enum lzc_send_flags flags ;
809
+ uint64_t resumeobj ;
810
+ uint64_t resumeoff ;
811
+ const char * redactbook ;
812
+ };
813
+
814
+ static int
815
+ lzc_send_resume_redacted_cb (int fd , void * arg )
816
+ {
817
+ struct lzc_send_resume_redacted * zsrr = arg ;
818
+ return (lzc_send_resume_redacted_cb_impl (zsrr -> snapname , zsrr -> from ,
819
+ fd , zsrr -> flags , zsrr -> resumeobj , zsrr -> resumeoff ,
820
+ zsrr -> redactbook ));
821
+ }
822
+
823
+ int
824
+ lzc_send_resume_redacted (const char * snapname , const char * from , int fd ,
825
+ enum lzc_send_flags flags , uint64_t resumeobj , uint64_t resumeoff ,
826
+ const char * redactbook )
827
+ {
828
+ struct lzc_send_resume_redacted zsrr = {
829
+ .snapname = snapname ,
830
+ .from = from ,
831
+ .flags = flags ,
832
+ .resumeobj = resumeobj ,
833
+ .resumeoff = resumeoff ,
834
+ .redactbook = redactbook ,
835
+ };
836
+ return (lzc_send_wrapper (lzc_send_resume_redacted_cb , fd , & zsrr ));
837
+ }
838
+
725
839
/*
726
840
* "from" can be NULL, a snapshot, or a bookmark.
727
841
*
@@ -737,9 +851,11 @@ lzc_send_resume_redacted(const char *snapname, const char *from, int fd,
737
851
* significantly more I/O and be less efficient than a send space estimation on
738
852
* an equivalent snapshot. This process is also used if redact_snaps is
739
853
* non-null.
854
+ *
855
+ * Pre-wrapped.
740
856
*/
741
- int
742
- lzc_send_space_resume_redacted (const char * snapname , const char * from ,
857
+ static int
858
+ lzc_send_space_resume_redacted_cb_impl (const char * snapname , const char * from ,
743
859
enum lzc_send_flags flags , uint64_t resumeobj , uint64_t resumeoff ,
744
860
uint64_t resume_bytes , const char * redactbook , int fd , uint64_t * spacep )
745
861
{
@@ -776,6 +892,45 @@ lzc_send_space_resume_redacted(const char *snapname, const char *from,
776
892
return (err );
777
893
}
778
894
895
+ struct lzc_send_space_resume_redacted {
896
+ const char * snapname ;
897
+ const char * from ;
898
+ enum lzc_send_flags flags ;
899
+ uint64_t resumeobj ;
900
+ uint64_t resumeoff ;
901
+ uint64_t resume_bytes ;
902
+ const char * redactbook ;
903
+ uint64_t * spacep ;
904
+ };
905
+
906
+ static int
907
+ lzc_send_space_resume_redacted_cb (int fd , void * arg )
908
+ {
909
+ struct lzc_send_space_resume_redacted * zssrr = arg ;
910
+ return (lzc_send_space_resume_redacted_cb_impl (zssrr -> snapname ,
911
+ zssrr -> from , zssrr -> flags , zssrr -> resumeobj , zssrr -> resumeoff ,
912
+ zssrr -> resume_bytes , zssrr -> redactbook , fd , zssrr -> spacep ));
913
+ }
914
+
915
+ int
916
+ lzc_send_space_resume_redacted (const char * snapname , const char * from ,
917
+ enum lzc_send_flags flags , uint64_t resumeobj , uint64_t resumeoff ,
918
+ uint64_t resume_bytes , const char * redactbook , int fd , uint64_t * spacep )
919
+ {
920
+ struct lzc_send_space_resume_redacted zssrr = {
921
+ .snapname = snapname ,
922
+ .from = from ,
923
+ .flags = flags ,
924
+ .resumeobj = resumeobj ,
925
+ .resumeoff = resumeoff ,
926
+ .resume_bytes = resume_bytes ,
927
+ .redactbook = redactbook ,
928
+ .spacep = spacep ,
929
+ };
930
+ return (lzc_send_wrapper (lzc_send_space_resume_redacted_cb ,
931
+ fd , & zssrr ));
932
+ }
933
+
779
934
int
780
935
lzc_send_space (const char * snapname , const char * from ,
781
936
enum lzc_send_flags flags , uint64_t * spacep )
0 commit comments