@@ -585,12 +585,246 @@ static int unix_gid_show(struct seq_file *m,
585585 return 0 ;
586586}
587587
588+ static int unix_gid_notify (struct cache_detail * cd , struct cache_head * h )
589+ {
590+ return sunrpc_cache_notify (cd , h , SUNRPC_CACHE_TYPE_UNIX_GID );
591+ }
592+
593+ /**
594+ * sunrpc_nl_unix_gid_get_reqs_dumpit - dump pending unix_gid requests
595+ * @skb: reply buffer
596+ * @cb: netlink metadata and command arguments
597+ *
598+ * Walk the unix_gid cache's pending request list and create a netlink
599+ * message with a nested entry for each cache_request, containing the
600+ * seqno and uid.
601+ *
602+ * Uses cb->args[0] as a seqno cursor for dump continuation across
603+ * multiple netlink messages.
604+ *
605+ * Returns the size of the reply or a negative errno.
606+ */
607+ int sunrpc_nl_unix_gid_get_reqs_dumpit (struct sk_buff * skb ,
608+ struct netlink_callback * cb )
609+ {
610+ struct sunrpc_net * sn ;
611+ struct cache_detail * cd ;
612+ struct cache_head * * items ;
613+ u64 * seqnos ;
614+ int cnt , i , emitted ;
615+ void * hdr ;
616+ int ret ;
617+
618+ sn = net_generic (sock_net (skb -> sk ), sunrpc_net_id );
619+
620+ cd = sn -> unix_gid_cache ;
621+ if (!cd )
622+ return - ENODEV ;
623+
624+ cnt = sunrpc_cache_requests_count (cd );
625+ if (!cnt )
626+ return 0 ;
627+
628+ items = kcalloc (cnt , sizeof (* items ), GFP_KERNEL );
629+ seqnos = kcalloc (cnt , sizeof (* seqnos ), GFP_KERNEL );
630+ if (!items || !seqnos ) {
631+ ret = - ENOMEM ;
632+ goto out_alloc ;
633+ }
634+
635+ cnt = sunrpc_cache_requests_snapshot (cd , items , seqnos , cnt ,
636+ cb -> args [0 ]);
637+ if (!cnt ) {
638+ ret = 0 ;
639+ goto out_alloc ;
640+ }
641+
642+ hdr = genlmsg_put (skb , NETLINK_CB (cb -> skb ).portid ,
643+ cb -> nlh -> nlmsg_seq , & sunrpc_nl_family ,
644+ NLM_F_MULTI , SUNRPC_CMD_UNIX_GID_GET_REQS );
645+ if (!hdr ) {
646+ ret = - ENOBUFS ;
647+ goto out_put ;
648+ }
649+
650+ emitted = 0 ;
651+ for (i = 0 ; i < cnt ; i ++ ) {
652+ struct unix_gid * ug ;
653+ struct nlattr * nest ;
654+
655+ ug = container_of (items [i ], struct unix_gid , h );
656+
657+ nest = nla_nest_start (skb ,
658+ SUNRPC_A_UNIX_GID_REQS_REQUESTS );
659+ if (!nest )
660+ break ;
661+
662+ if (nla_put_u64_64bit (skb , SUNRPC_A_UNIX_GID_SEQNO ,
663+ seqnos [i ], 0 ) ||
664+ nla_put_u32 (skb , SUNRPC_A_UNIX_GID_UID ,
665+ from_kuid (& init_user_ns , ug -> uid ))) {
666+ nla_nest_cancel (skb , nest );
667+ break ;
668+ }
669+
670+ nla_nest_end (skb , nest );
671+ cb -> args [0 ] = seqnos [i ];
672+ emitted ++ ;
673+ }
674+
675+ if (!emitted ) {
676+ genlmsg_cancel (skb , hdr );
677+ ret = - EMSGSIZE ;
678+ goto out_put ;
679+ }
680+
681+ genlmsg_end (skb , hdr );
682+ ret = skb -> len ;
683+ out_put :
684+ for (i = 0 ; i < cnt ; i ++ )
685+ cache_put (items [i ], cd );
686+ out_alloc :
687+ kfree (seqnos );
688+ kfree (items );
689+ return ret ;
690+ }
691+
692+ /**
693+ * sunrpc_nl_parse_one_unix_gid - parse one unix_gid entry from netlink
694+ * @cd: cache_detail for the unix_gid cache
695+ * @attr: nested attribute containing unix_gid fields
696+ *
697+ * Parses one unix_gid entry from a netlink message and updates the
698+ * cache. Mirrors the logic in unix_gid_parse().
699+ *
700+ * Returns 0 on success or a negative errno.
701+ */
702+ static int sunrpc_nl_parse_one_unix_gid (struct cache_detail * cd ,
703+ struct nlattr * attr )
704+ {
705+ struct nlattr * tb [SUNRPC_A_UNIX_GID_EXPIRY + 1 ];
706+ struct unix_gid ug , * ugp ;
707+ struct timespec64 boot ;
708+ struct nlattr * gid_attr ;
709+ int err , rem , gids = 0 ;
710+ kuid_t uid ;
711+
712+ err = nla_parse_nested (tb , SUNRPC_A_UNIX_GID_EXPIRY , attr ,
713+ sunrpc_unix_gid_nl_policy , NULL );
714+ if (err )
715+ return err ;
716+
717+ /* uid (required) */
718+ if (!tb [SUNRPC_A_UNIX_GID_UID ])
719+ return - EINVAL ;
720+ uid = make_kuid (current_user_ns (),
721+ nla_get_u32 (tb [SUNRPC_A_UNIX_GID_UID ]));
722+ ug .uid = uid ;
723+
724+ /* expiry (required, wallclock seconds) */
725+ if (!tb [SUNRPC_A_UNIX_GID_EXPIRY ])
726+ return - EINVAL ;
727+ getboottime64 (& boot );
728+ ug .h .flags = 0 ;
729+ ug .h .expiry_time = nla_get_u64 (tb [SUNRPC_A_UNIX_GID_EXPIRY ]) -
730+ boot .tv_sec ;
731+
732+ if (tb [SUNRPC_A_UNIX_GID_NEGATIVE ]) {
733+ ug .gi = groups_alloc (0 );
734+ if (!ug .gi )
735+ return - ENOMEM ;
736+ } else {
737+ /* Count gids */
738+ nla_for_each_nested_type (gid_attr , SUNRPC_A_UNIX_GID_GIDS ,
739+ attr , rem )
740+ gids ++ ;
741+
742+ if (gids > 8192 )
743+ return - EINVAL ;
744+
745+ ug .gi = groups_alloc (gids );
746+ if (!ug .gi )
747+ return - ENOMEM ;
748+
749+ gids = 0 ;
750+ nla_for_each_nested_type (gid_attr , SUNRPC_A_UNIX_GID_GIDS ,
751+ attr , rem ) {
752+ kgid_t kgid ;
753+
754+ kgid = make_kgid (current_user_ns (),
755+ nla_get_u32 (gid_attr ));
756+ if (!gid_valid (kgid )) {
757+ err = - EINVAL ;
758+ goto out ;
759+ }
760+ ug .gi -> gid [gids ++ ] = kgid ;
761+ }
762+ groups_sort (ug .gi );
763+ }
764+
765+ ugp = unix_gid_lookup (cd , uid );
766+ if (ugp ) {
767+ struct cache_head * ch ;
768+
769+ ch = sunrpc_cache_update (cd , & ug .h , & ugp -> h ,
770+ unix_gid_hash (uid ));
771+ if (!ch ) {
772+ err = - ENOMEM ;
773+ } else {
774+ err = 0 ;
775+ cache_put (ch , cd );
776+ }
777+ } else {
778+ err = - ENOMEM ;
779+ }
780+ out :
781+ if (ug .gi )
782+ put_group_info (ug .gi );
783+ return err ;
784+ }
785+
786+ /**
787+ * sunrpc_nl_unix_gid_set_reqs_doit - respond to unix_gid requests
788+ * @skb: reply buffer
789+ * @info: netlink metadata and command arguments
790+ *
791+ * Parse one or more unix_gid cache responses from userspace and
792+ * update the unix_gid cache accordingly.
793+ *
794+ * Returns 0 on success or a negative errno.
795+ */
796+ int sunrpc_nl_unix_gid_set_reqs_doit (struct sk_buff * skb ,
797+ struct genl_info * info )
798+ {
799+ struct sunrpc_net * sn ;
800+ struct cache_detail * cd ;
801+ const struct nlattr * attr ;
802+ int rem , ret = 0 ;
803+
804+ sn = net_generic (genl_info_net (info ), sunrpc_net_id );
805+
806+ cd = sn -> unix_gid_cache ;
807+ if (!cd )
808+ return - ENODEV ;
809+
810+ nlmsg_for_each_attr_type (attr , SUNRPC_A_UNIX_GID_REQS_REQUESTS ,
811+ info -> nlhdr , GENL_HDRLEN , rem ) {
812+ ret = sunrpc_nl_parse_one_unix_gid (cd ,
813+ (struct nlattr * )attr );
814+ if (ret )
815+ break ;
816+ }
817+
818+ return ret ;
819+ }
820+
588821static const struct cache_detail unix_gid_cache_template = {
589822 .owner = THIS_MODULE ,
590823 .hash_size = GID_HASHMAX ,
591824 .name = "auth.unix.gid" ,
592825 .cache_put = unix_gid_put ,
593826 .cache_upcall = unix_gid_upcall ,
827+ .cache_notify = unix_gid_notify ,
594828 .cache_request = unix_gid_request ,
595829 .cache_parse = unix_gid_parse ,
596830 .cache_show = unix_gid_show ,
0 commit comments