Skip to main content

c_gull/
nss.rs

1//! Name Service Switch functions.
2//!
3//! In order to avoid implementing `dlopen`, while still correctly implementing
4//! fully /etc/nsswitch.conf-respecting NSS functionality, we invoke the
5//! `getent` command and parse its output.
6//!
7//! This file doesn't yet implement enumeration, but the `getent` command does,
8//! so it's theoretically doable.
9
10use core::cell::SyncUnsafeCell;
11use core::ffi::CStr;
12use core::mem::{align_of, zeroed};
13use core::ptr::{addr_of_mut, copy_nonoverlapping, null, null_mut, write};
14use core::str;
15use core::str::FromStr;
16use errno::{set_errno, Errno};
17use libc::{c_char, c_int, c_void, gid_t, group, passwd, size_t, uid_t};
18use rustix::path::DecInt;
19use std::ffi::OsStr;
20use std::os::unix::ffi::OsStrExt;
21use std::process::Command;
22
23#[no_mangle]
24unsafe extern "C" fn getpwnam_r(
25    name: *const c_char,
26    pwd: *mut passwd,
27    buf: *mut c_char,
28    buflen: usize,
29    result: *mut *mut passwd,
30) -> c_int {
31    libc!(libc::getpwnam_r(name, pwd, buf, buflen, result));
32
33    let name = OsStr::from_bytes(CStr::from_ptr(name).to_bytes());
34    let mut command = Command::new("getent");
35    command.arg("passwd").arg(name);
36    getpw_r(command, pwd, buf, buflen, result)
37}
38
39#[no_mangle]
40unsafe extern "C" fn getpwuid_r(
41    uid: uid_t,
42    pwd: *mut passwd,
43    buf: *mut c_char,
44    buflen: usize,
45    result: *mut *mut passwd,
46) -> c_int {
47    libc!(libc::getpwuid_r(uid, pwd, buf, buflen, result));
48
49    let dec_int = DecInt::new(uid);
50    let name = OsStr::from_bytes(dec_int.as_bytes());
51    let mut command = Command::new("getent");
52    command.arg("passwd").arg(name);
53    getpw_r(command, pwd, buf, buflen, result)
54}
55
56#[no_mangle]
57unsafe extern "C" fn getgrnam_r(
58    name: *const c_char,
59    grp: *mut group,
60    buf: *mut c_char,
61    buflen: usize,
62    result: *mut *mut group,
63) -> c_int {
64    libc!(libc::getgrnam_r(name, grp, buf, buflen, result));
65
66    let name = OsStr::from_bytes(CStr::from_ptr(name).to_bytes());
67    let mut command = Command::new("getent");
68    command.arg("group").arg(name);
69    getgr_r(command, grp, buf, buflen, result)
70}
71
72#[no_mangle]
73unsafe extern "C" fn getgrgid_r(
74    gid: gid_t,
75    grp: *mut group,
76    buf: *mut c_char,
77    buflen: usize,
78    result: *mut *mut group,
79) -> c_int {
80    libc!(libc::getgrgid_r(gid, grp, buf, buflen, result));
81
82    let dec_int = DecInt::new(gid);
83    let name = OsStr::from_bytes(dec_int.as_bytes());
84    let mut command = Command::new("getent");
85    command.arg("group").arg(name);
86    getgr_r(command, grp, buf, buflen, result)
87}
88
89unsafe fn getpw_r(
90    command: Command,
91    pwd: *mut passwd,
92    buf: *mut c_char,
93    buflen: usize,
94    result: *mut *mut passwd,
95) -> c_int {
96    let mut command = command;
97    let output = match command.output() {
98        Ok(output) => output,
99        Err(_err) => return parse_error(result.cast()),
100    };
101
102    match output.status.code() {
103        Some(0) => {}
104        Some(2) => return success(result.cast(), null_mut()),
105        Some(r) => panic!("unexpected exit status from `getent passwd`: {}", r),
106        None => return parse_error(result.cast()),
107    }
108
109    let stdout = match str::from_utf8(&output.stdout) {
110        Ok(stdout) => stdout,
111        Err(_err) => return parse_error(result.cast()),
112    };
113    let stdout = match stdout.strip_suffix('\n') {
114        Some(stdout) => stdout,
115        None => return parse_error(result.cast()),
116    };
117
118    let mut parts = stdout.split(':');
119    let mut buf = buf;
120    let mut buflen = buflen;
121
122    let part = match parts.next() {
123        Some(part) => part,
124        None => return parse_error(result.cast()),
125    };
126    if part.len() > buflen {
127        return buffer_exhausted(result.cast());
128    }
129    let pw_name = buf;
130    copy_nonoverlapping(part.as_ptr(), buf.cast(), part.len());
131    buf = buf.add(part.len());
132    write(buf, 0);
133    buf = buf.add(1);
134    buflen -= part.len() + 1;
135
136    let part = match parts.next() {
137        Some(part) => part,
138        None => return parse_error(result.cast()),
139    };
140    if part.len() > buflen {
141        return buffer_exhausted(result.cast());
142    }
143    let pw_passwd = buf;
144    copy_nonoverlapping(part.as_ptr(), buf.cast(), part.len());
145    buf = buf.add(part.len());
146    write(buf, 0);
147    buf = buf.add(1);
148    buflen -= part.len() + 1;
149
150    let part = match parts.next() {
151        Some(part) => part,
152        None => return parse_error(result.cast()),
153    };
154    let pw_uid = match part.parse() {
155        Ok(pw_uid) => pw_uid,
156        Err(_err) => return parse_error(result.cast()),
157    };
158
159    let part = match parts.next() {
160        Some(part) => part,
161        None => return parse_error(result.cast()),
162    };
163    let pw_gid = match part.parse() {
164        Ok(pw_gid) => pw_gid,
165        Err(_err) => return parse_error(result.cast()),
166    };
167
168    let part = match parts.next() {
169        Some(part) => part,
170        None => return parse_error(result.cast()),
171    };
172    if part.len() > buflen {
173        return buffer_exhausted(result.cast());
174    }
175    let pw_gecos = buf;
176    copy_nonoverlapping(part.as_ptr(), buf.cast(), part.len());
177    buf = buf.add(part.len());
178    write(buf, 0);
179    buf = buf.add(1);
180    buflen -= part.len() + 1;
181
182    let part = match parts.next() {
183        Some(part) => part,
184        None => return parse_error(result.cast()),
185    };
186    if part.len() > buflen {
187        return buffer_exhausted(result.cast());
188    }
189    let pw_dir = buf;
190    copy_nonoverlapping(part.as_ptr(), buf.cast(), part.len());
191    buf = buf.add(part.len());
192    write(buf, 0);
193    buf = buf.add(1);
194    buflen -= part.len() + 1;
195
196    let part = match parts.next() {
197        Some(part) => part,
198        None => return parse_error(result.cast()),
199    };
200    if part.len() > buflen {
201        return buffer_exhausted(result.cast());
202    }
203    let pw_shell = buf;
204    copy_nonoverlapping(part.as_ptr(), buf.cast(), part.len());
205    buf = buf.add(part.len());
206    write(buf, 0);
207
208    write(
209        pwd,
210        passwd {
211            pw_name,
212            pw_passwd,
213            pw_uid,
214            pw_gid,
215            pw_gecos,
216            pw_dir,
217            pw_shell,
218        },
219    );
220    success(result.cast(), pwd.cast())
221}
222
223unsafe fn getgr_r(
224    command: Command,
225    grp: *mut group,
226    buf: *mut c_char,
227    buflen: usize,
228    result: *mut *mut group,
229) -> c_int {
230    let mut command = command;
231    let output = match command.output() {
232        Ok(output) => output,
233        Err(_err) => return parse_error(result.cast()),
234    };
235
236    match output.status.code() {
237        Some(0) => {}
238        Some(2) => return success(result.cast(), null_mut()),
239        Some(r) => panic!("unexpected exit status from `getent group`: {}", r),
240        None => return parse_error(result.cast()),
241    }
242
243    let stdout = match str::from_utf8(&output.stdout) {
244        Ok(stdout) => stdout,
245        Err(_err) => return parse_error(result.cast()),
246    };
247    let stdout = match stdout.strip_suffix('\n') {
248        Some(stdout) => stdout,
249        None => return parse_error(result.cast()),
250    };
251
252    let mut parts = stdout.split(':');
253    let mut buf = buf;
254    let mut buflen = buflen;
255
256    let part = match parts.next() {
257        Some(part) => part,
258        None => return parse_error(result.cast()),
259    };
260    if part.len() > buflen {
261        return buffer_exhausted(result.cast());
262    }
263    let gr_name = buf;
264    copy_nonoverlapping(part.as_ptr(), buf.cast(), part.len());
265    buf = buf.add(part.len());
266    write(buf, 0);
267    buf = buf.add(1);
268    buflen -= part.len() + 1;
269
270    let part = match parts.next() {
271        Some(part) => part,
272        None => return parse_error(result.cast()),
273    };
274    if part.len() > buflen {
275        return buffer_exhausted(result.cast());
276    }
277    let gr_passwd = buf;
278    copy_nonoverlapping(part.as_ptr(), buf.cast(), part.len());
279    buf = buf.add(part.len());
280    write(buf, 0);
281    buf = buf.add(1);
282    buflen -= part.len() + 1;
283
284    let part = match parts.next() {
285        Some(part) => part,
286        None => return parse_error(result.cast()),
287    };
288    let gr_gid = match part.parse() {
289        Ok(pw_gid) => pw_gid,
290        Err(_err) => return parse_error(result.cast()),
291    };
292
293    let part = match parts.next() {
294        Some(part) => part,
295        None => return parse_error(result.cast()),
296    };
297    if part.len() > buflen {
298        return buffer_exhausted(result.cast());
299    }
300
301    let num_members = if part.is_empty() {
302        0
303    } else {
304        part.split(',').count()
305    };
306    let pad = align_of::<*const c_char>() - (buf.addr()) % align_of::<*const c_char>();
307    buf = buf.add(pad);
308    let gr_mem = buf.cast::<*mut c_char>();
309    buf = gr_mem.add(num_members + 1).cast::<c_char>();
310
311    let mut cur_mem = gr_mem;
312    if num_members != 0 {
313        for member in part.split(',') {
314            *cur_mem = buf;
315            cur_mem = cur_mem.add(1);
316            copy_nonoverlapping(member.as_ptr(), buf.cast(), member.len());
317            buf = buf.add(member.len());
318            write(buf, 0);
319            buf = buf.add(1);
320        }
321    }
322    write(cur_mem, null_mut());
323
324    write(
325        grp,
326        group {
327            gr_name,
328            gr_passwd,
329            gr_gid,
330            gr_mem,
331        },
332    );
333    success(result.cast(), grp.cast())
334}
335
336#[cold]
337unsafe fn buffer_exhausted(result: *mut *mut c_void) -> c_int {
338    *result = null_mut();
339    // It isn't documented that the `_r` functions set `errno` in addition to
340    // returning it, but other popular implementations do, so set it.
341    set_errno(Errno(libc::ERANGE));
342    libc::ERANGE
343}
344
345#[cold]
346unsafe fn parse_error(result: *mut *mut c_void) -> c_int {
347    *result = null_mut();
348    // As above, also set `errno`.
349    set_errno(Errno(libc::EIO));
350    libc::EIO
351}
352
353unsafe fn success(result: *mut *mut c_void, value: *mut c_void) -> c_int {
354    *result = value;
355    // As above, also set `errno`. Explicitly set it to zero in case any
356    // intermediate operations failed.
357    set_errno(Errno(0));
358    0
359}
360
361struct StaticPasswd {
362    record: passwd,
363    buf: *mut c_char,
364    len: usize,
365}
366// The C contract is that it's the caller's responsibility to ensure that
367// we don't implicitly send this across threads.
368unsafe impl Sync for StaticPasswd {}
369static STATIC_PASSWD: SyncUnsafeCell<StaticPasswd> = SyncUnsafeCell::new(StaticPasswd {
370    record: passwd {
371        pw_name: null_mut(),
372        pw_passwd: null_mut(),
373        pw_uid: 0,
374        pw_gid: 0,
375        pw_gecos: null_mut(),
376        pw_dir: null_mut(),
377        pw_shell: null_mut(),
378    },
379    buf: null_mut(),
380    len: 0,
381});
382
383struct StaticGroup {
384    record: group,
385    buf: *mut c_char,
386    len: usize,
387}
388// The C contract is that it's the caller's responsibility to ensure that
389// we don't implicitly send this across threads.
390unsafe impl Sync for StaticGroup {}
391static STATIC_GROUP: SyncUnsafeCell<StaticGroup> = SyncUnsafeCell::new(StaticGroup {
392    record: group {
393        gr_name: null_mut(),
394        gr_passwd: null_mut(),
395        gr_gid: 0,
396        gr_mem: null_mut(),
397    },
398    buf: null_mut(),
399    len: 0,
400});
401
402#[no_mangle]
403unsafe extern "C" fn getpwnam(name: *const c_char) -> *mut libc::passwd {
404    libc!(libc::getpwnam(name));
405
406    let static_passwd = &mut *STATIC_PASSWD.get();
407    let mut ptr: *mut libc::passwd = &mut static_passwd.record;
408
409    loop {
410        if static_passwd.len == 0 {
411            static_passwd.len = 1024;
412        } else {
413            static_passwd.len *= 2;
414            libc::free(static_passwd.buf.cast());
415        }
416
417        static_passwd.buf = libc::malloc(static_passwd.len).cast();
418        if static_passwd.buf.is_null() {
419            static_passwd.len = 0;
420            set_errno(Errno(libc::ENOMEM));
421            return null_mut();
422        }
423
424        let r = getpwnam_r(
425            name,
426            &mut static_passwd.record,
427            static_passwd.buf,
428            static_passwd.len,
429            &mut ptr,
430        );
431        if r == 0 {
432            return ptr;
433        }
434        if r != libc::ERANGE {
435            return null_mut();
436        }
437    }
438}
439
440#[no_mangle]
441unsafe extern "C" fn getpwuid(uid: uid_t) -> *mut libc::passwd {
442    libc!(libc::getpwuid(uid));
443
444    let static_passwd = &mut *STATIC_PASSWD.get();
445    let mut ptr: *mut libc::passwd = &mut static_passwd.record;
446
447    loop {
448        if static_passwd.len == 0 {
449            static_passwd.len = 1024;
450        } else {
451            static_passwd.len *= 2;
452            libc::free(static_passwd.buf.cast());
453        }
454
455        static_passwd.buf = libc::malloc(static_passwd.len).cast();
456        if static_passwd.buf.is_null() {
457            static_passwd.len = 0;
458            set_errno(Errno(libc::ENOMEM));
459            return null_mut();
460        }
461
462        let r = getpwuid_r(
463            uid,
464            &mut static_passwd.record,
465            static_passwd.buf,
466            static_passwd.len,
467            &mut ptr,
468        );
469        if r == 0 {
470            return ptr;
471        }
472        if r != libc::ERANGE {
473            return null_mut();
474        }
475    }
476}
477
478#[no_mangle]
479unsafe extern "C" fn getgrnam(name: *const c_char) -> *mut libc::group {
480    libc!(libc::getgrnam(name));
481
482    let static_group = &mut *STATIC_GROUP.get();
483    let mut ptr: *mut libc::group = &mut static_group.record;
484
485    loop {
486        if static_group.len == 0 {
487            static_group.len = 1024;
488        } else {
489            static_group.len *= 2;
490            libc::free(static_group.buf.cast());
491        }
492
493        static_group.buf = libc::malloc(static_group.len).cast();
494        if static_group.buf.is_null() {
495            static_group.len = 0;
496            set_errno(Errno(libc::ENOMEM));
497            return null_mut();
498        }
499
500        let r = getgrnam_r(
501            name,
502            &mut static_group.record,
503            static_group.buf,
504            static_group.len,
505            &mut ptr,
506        );
507        if r == 0 {
508            return ptr;
509        }
510        if r != libc::ERANGE {
511            return null_mut();
512        }
513    }
514}
515
516#[no_mangle]
517unsafe extern "C" fn getgrgid(gid: gid_t) -> *mut libc::group {
518    libc!(libc::getgrgid(gid));
519
520    let static_group = &mut *STATIC_GROUP.get();
521    let mut ptr: *mut libc::group = &mut static_group.record;
522
523    loop {
524        if static_group.len == 0 {
525            static_group.len = 1024;
526        } else {
527            static_group.len *= 2;
528            libc::free(static_group.buf.cast());
529        }
530
531        static_group.buf = libc::malloc(static_group.len).cast();
532        if static_group.buf.is_null() {
533            static_group.len = 0;
534            set_errno(Errno(libc::ENOMEM));
535            return null_mut();
536        }
537
538        let r = getgrgid_r(
539            gid,
540            &mut static_group.record,
541            static_group.buf,
542            static_group.len,
543            &mut ptr,
544        );
545        if r == 0 {
546            return ptr;
547        }
548        if r != libc::ERANGE {
549            return null_mut();
550        }
551    }
552}
553
554#[no_mangle]
555unsafe extern "C" fn getgrouplist(
556    user: *const c_char,
557    group: gid_t,
558    groups: *mut gid_t,
559    ngroups: *mut c_int,
560) -> c_int {
561    libc!(libc::getgrouplist(user, group, groups, ngroups));
562
563    let user = OsStr::from_bytes(CStr::from_ptr(user).to_bytes());
564    let mut groups = groups;
565
566    let mut command = Command::new("getent");
567    command.arg("initgroups").arg(user);
568
569    let output = match command.output() {
570        Ok(output) => output,
571        Err(_err) => return -1,
572    };
573
574    match output.status.code() {
575        Some(0) => {}
576        Some(r) => panic!("unexpected exit status from `getent initgroups`: {}", r),
577        None => return -1,
578    }
579
580    let stdout = match str::from_utf8(&output.stdout) {
581        Ok(stdout) => stdout,
582        Err(_err) => return -1,
583    };
584    let stdout = match stdout.strip_suffix('\n') {
585        Some(stdout) => stdout,
586        None => return -1,
587    };
588
589    let mut parts = stdout.split_ascii_whitespace();
590    match parts.next() {
591        Some(part) => {
592            if part != user {
593                return -1;
594            }
595        }
596        None => return -1,
597    };
598
599    let ngroups_in = ngroups.read();
600    let mut ngroups_out = 0;
601
602    if ngroups_out == ngroups_in {
603        return -1;
604    }
605    ngroups_out += 1;
606    groups.write(group);
607    groups = groups.add(1);
608
609    for part in parts {
610        let gid: u32 = match part.parse() {
611            Ok(gid) => gid,
612            Err(_) => return -1,
613        };
614        if gid == group {
615            continue;
616        }
617        if ngroups_out == ngroups_in {
618            return -1;
619        }
620        ngroups_out += 1;
621        groups.write(gid);
622        groups = groups.add(1);
623    }
624
625    ngroups.write(ngroups_out);
626    ngroups_out
627}
628
629#[no_mangle]
630unsafe extern "C" fn getservbyport_r(
631    port: c_int,
632    proto: *const c_char,
633    result_buf: *mut libc::servent,
634    buf: *mut c_char,
635    buflen: size_t,
636    result: *mut *mut libc::servent,
637) -> c_int {
638    //libc!(libc::getservbyport_r(
639    //port, proto, result_buf, buf, buflen, result
640    //));
641
642    let mut command = Command::new("getent");
643    command
644        .arg("services")
645        .arg(DecInt::new(u16::from_be(port as u16)).as_ref());
646    getserv_r(command, null(), proto, result_buf, buf, buflen, result)
647}
648
649#[no_mangle]
650unsafe extern "C" fn getservbyname_r(
651    name: *const c_char,
652    proto: *const c_char,
653    result_buf: *mut libc::servent,
654    buf: *mut c_char,
655    buflen: size_t,
656    result: *mut *mut libc::servent,
657) -> c_int {
658    //libc!(libc::getservbyname_r(
659    //name, proto, result_buf, buf, buflen, result
660    //));
661
662    let arg_name = OsStr::from_bytes(CStr::from_ptr(name).to_bytes());
663    let mut command = Command::new("getent");
664    command.arg("services").arg(arg_name);
665    getserv_r(command, name, proto, result_buf, buf, buflen, result)
666}
667
668unsafe fn getserv_r(
669    command: Command,
670    name: *const c_char,
671    proto: *const c_char,
672    result_buf: *mut libc::servent,
673    buf: *mut c_char,
674    buflen: size_t,
675    result: *mut *mut libc::servent,
676) -> c_int {
677    // glibc returns all the aliases but doesn't include the protocol name, and
678    // musl returns just the protocol name as the alias list. The intersection
679    // of these two that portable code is obliged to assume is an empty list.
680    static mut STATIC_SERVENT_ALIASES: *mut c_char = null_mut();
681    let s_aliases = &mut *addr_of_mut!(STATIC_SERVENT_ALIASES);
682
683    let mut command = command;
684    let output = match command.output() {
685        Ok(output) => output,
686        Err(_err) => {
687            *result = null_mut();
688            return libc::EIO;
689        }
690    };
691
692    match output.status.code() {
693        Some(0) => {}
694        Some(2) => {
695            *result = null_mut();
696            return libc::ENOENT;
697        }
698        Some(r) => panic!("unexpected exit status from `getent services`: {}", r),
699        None => {
700            *result = null_mut();
701            return libc::EIO;
702        }
703    }
704
705    let stdout = match str::from_utf8(&output.stdout) {
706        Ok(stdout) => stdout,
707        Err(_err) => {
708            *result = null_mut();
709            return libc::EIO;
710        }
711    };
712    let stdout = match stdout.strip_suffix('\n') {
713        Some(stdout) => stdout,
714        None => {
715            *result = null_mut();
716            return libc::EIO;
717        }
718    };
719
720    // Parse eg. "http                  80/tcp www".
721    let mut parts = stdout.split_ascii_whitespace();
722
723    let s_name = match parts.next() {
724        Some(check_name) => {
725            if name.is_null() {
726                if check_name.len() > buflen {
727                    return libc::ERANGE;
728                }
729                copy_nonoverlapping(check_name.as_ptr(), buf.cast(), check_name.len());
730                buf.add(check_name.len()).write(0);
731                buf
732            } else {
733                name.cast_mut()
734            }
735        }
736        None => {
737            *result = null_mut();
738            return libc::EIO;
739        }
740    };
741
742    let port_protocol = match parts.next() {
743        Some(port_protocol) => port_protocol,
744        None => {
745            *result = null_mut();
746            return libc::EIO;
747        }
748    };
749
750    // The rest of `pars.next()` contains aliases, but per the comment above,
751    // we ignore them.
752
753    // Parse eg. "443/tcp".
754    let (port, protocol) = match port_protocol.split_once('/') {
755        Some(port_protocol) => port_protocol,
756        None => {
757            *result = null_mut();
758            return libc::EIO;
759        }
760    };
761
762    // Parse the port number.
763    let s_port: i32 = match u16::from_str(port) {
764        Ok(port) => port.to_be().into(),
765        Err(_) => {
766            *result = null_mut();
767            return libc::EIO;
768        }
769    };
770
771    // Check the protocol, and if needed, translate the protocol string to
772    // a static string so that it lives at least as long as the `servent`.
773    let s_proto = if !proto.is_null() {
774        if protocol.as_bytes() != CStr::from_ptr(proto).to_bytes() {
775            *result = null_mut();
776            return libc::EIO;
777        }
778        proto
779    } else if protocol == "tcp" {
780        c"tcp".as_ptr()
781    } else if protocol == "udp" {
782        c"udp".as_ptr()
783    } else {
784        return libc::EINVAL;
785    }
786    .cast_mut();
787
788    *result_buf = libc::servent {
789        s_name,
790        s_aliases,
791        s_port,
792        s_proto,
793    };
794    *result = result_buf;
795    0
796}
797
798// The C contract is that it's the caller's responsibility to ensure that
799// we don't implicitly send this across threads.
800static mut STATIC_SERVENT: libc::servent = unsafe { zeroed() };
801
802#[no_mangle]
803unsafe extern "C" fn getservbyname(
804    name: *const c_char,
805    proto: *const c_char,
806) -> *mut libc::servent {
807    libc!(libc::getservbyname(name, proto));
808
809    let mut result = null_mut();
810    if getservbyname_r(
811        name,
812        proto,
813        addr_of_mut!(STATIC_SERVENT),
814        null_mut(),
815        0,
816        &mut result,
817    ) == 0
818    {
819        result
820    } else {
821        null_mut()
822    }
823}
824
825#[no_mangle]
826unsafe extern "C" fn getservbyport(port: c_int, proto: *const c_char) -> *mut libc::servent {
827    libc!(libc::getservbyport(port, proto));
828
829    let mut buf = [0; 32];
830    let mut result = null_mut();
831    if getservbyport_r(
832        port,
833        proto,
834        addr_of_mut!(STATIC_SERVENT),
835        buf.as_mut_ptr(),
836        buf.len(),
837        &mut result,
838    ) == 0
839    {
840        result
841    } else {
842        null_mut()
843    }
844}