History

[clone] a better workaround for the 'drop scope' issue

Sebastian Thiel committed 6 years ago

Commit 3ccf32b1 parent 6b479231 file · +28 −56Browse files

Unified · Split

git-protocol/src/fetch.rs+28−56
@@ -84,36 +84,6 @@ impl From<client::Capabilities> for Capabilities { · 83 unmodified lines
84// V184// V1
85// 0098want 808e50d724f604f69ab93c6da2919c014667bedb multi_ack_detailed no-done side-band-64k thin-pack ofs-delta deepen-since deepen-not agent=git/2.28.085// 0098want 808e50d724f604f69ab93c6da2919c014667bedb multi_ack_detailed no-done side-band-64k thin-pack ofs-delta deepen-since deepen-not agent=git/2.28.0
86 86
87/// This is only needed because for some reason, a match statement takes the drop scope of the enclosing scope, and not of
88/// the match arm. This makes it think that a borrowed Ok(value) is still in scope, even though we are in the Err(err) branch.
89/// The idea here is that we can workaround this by setting the scope to the level of the function, by splitting everything up accordingly.
90/// Tracking issue: https://github.com/rust-lang/rust/issues/76149 - discarded, as currently documented/known behaviour.
91fn perform_handshake<'a, F: FnMut(credentials::Action) -> credentials::Result>(
92 transport: &'a mut impl client::Transport,
93 delegate: &mut impl Delegate,
94 mut authenticate: Option<&mut F>,
95 out: &mut Option<credentials::NextAction>,
96) -> Result<client::SetServiceResponse<'a>, Error> {
97 if let Some(authenticate) = authenticate.as_mut() {
98 let url = transport.to_url();
99 let credentials::Outcome { identity, next } = authenticate(credentials::Action::Fill(&url))?;
100 transport.set_identity(identity)?;
101 // Remember the output of the authentication function to provide in case we still error with
102 // a permission issue.
103 *out = Some(next);
104 };
105
106 let response = transport.handshake(Service::UploadPack)?;
107 if let Some(authenticate) = authenticate {
108 authenticate(
109 out.take()
110 .expect("we put next action in before if an authenticator is present")
111 .approve(),
112 )?;
113 }
114 Ok(response)
115}
116
117/// This types sole purpose is to 'disable' the destructor on the Box provided in the `SetServiceResponse` type87/// This types sole purpose is to 'disable' the destructor on the Box provided in the `SetServiceResponse` type
118/// by leaking the box. We provide a method to restore the box and drop it right away to not actually leak.88/// by leaking the box. We provide a method to restore the box and drop it right away to not actually leak.
119/// However, we do leak in error cases because we don't call the manual destructor then.89/// However, we do leak in error cases because we don't call the manual destructor then.
@@ -140,7 +110,9 @@ impl<'a> LeakedSetServiceResponse<'a> { · 20 unmodified lines
140 if let Some(b) = self.refs.take() {110 if let Some(b) = self.refs.take() {
141 // SAFETY: We are bound to lifetime 'a, which is the lifetime of the thing pointed to by the trait object in the box.111 // SAFETY: We are bound to lifetime 'a, which is the lifetime of the thing pointed to by the trait object in the box.
142 // Thus we can only drop the box while that thing is indeed valid, due to Rusts standard lifetime rules.112 // Thus we can only drop the box while that thing is indeed valid, due to Rusts standard lifetime rules.
143 // The box itself was leaked by us113 // The box itself was leaked by us.
114 // Note that this is only required because Drop scopes are the outer ones in the match, not the match arms, making them
115 // too broad to be usable intuitively. I consider this a technical shortcoming and hope there is a way to resolve it.
144 #[allow(unsafe_code)]116 #[allow(unsafe_code)]
145 unsafe {117 unsafe {
146 drop(Box::from_raw(b as *mut _))118 drop(Box::from_raw(b as *mut _))
@@ -154,32 +126,32 @@ pub fn fetch<F: FnMut(credentials::Action) -> credentials::Result>( · 7 unmodified lines
154 mut delegate: impl Delegate,126 mut delegate: impl Delegate,
155 mut authenticate: F,127 mut authenticate: F,
156) -> Result<(), Error> {128) -> Result<(), Error> {
157 let mut next = None;129 let mut res: LeakedSetServiceResponse = match transport.handshake(Service::UploadPack).map(Into::into) {
158 let mut res: LeakedSetServiceResponse =130 Ok(v) => Ok(v),
159 match perform_handshake(&mut transport, &mut delegate, None::<&mut F>, &mut next).map(Into::into) {131 Err(client::Error::Io { err }) if err.kind() == io::ErrorKind::PermissionDenied => {
160 Ok(v) => Ok(v),132 let url = transport.to_url();
161 Err(Error::Transport(client::Error::Io { err })) if err.kind() == io::ErrorKind::PermissionDenied => {133 let credentials::Outcome { identity, next } = authenticate(credentials::Action::Fill(&url))?;
162 perform_handshake(&mut transport, &mut delegate, Some(&mut authenticate), &mut next)134 transport.set_identity(identity)?;
163 .map_err(|err| {135 match transport.handshake(Service::UploadPack).map(Into::into) {
164 if let Some(next) = next {136 Ok(v) => {
165 match &err {137 authenticate(next.approve())?;
166 // Still no permission? Reject the credentials138 Ok(v)
167 Error::Transport(client::Error::Io { err })139 }
168 if err.kind() == io::ErrorKind::PermissionDenied =>140 // Still no permission? Reject the credentials.
169 {141 Err(client::Error::Io { err }) if err.kind() == io::ErrorKind::PermissionDenied => {
170 authenticate(next.reject())142 authenticate(next.reject())?;
171 }143 Err(client::Error::Io { err }.into())
172 // Otherwise it's some other error, still OK to approve the credentials144 }
173 _ => authenticate(next.approve()),145 // Otherwise it's some other error, still OK to approve the credentials. We also do this to not accidentally
174 }146 // discard credentials that have been previously stored.
175 .ok();147 Err(err) => {
176 };148 authenticate(next.approve())?;
177 err149 Err(err.into())
178 })150 }
179 .map(Into::into)
180 }151 }
181 Err(err) => Err(err),152 }
182 }?;153 Err(err) => Err(err),
154 }?;
183 155
184 delegate.adjust_capabilities(res.actual_protocol, &mut res.capabilities);156 delegate.adjust_capabilities(res.actual_protocol, &mut res.capabilities);
185 res.capabilities.set_agent_version();157 res.capabilities.set_agent_version();