162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* scm.c - Socket level control messages processing. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Author: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 562306a36Sopenharmony_ci * Alignment and value checking mods by Craig Metz 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/signal.h> 1062306a36Sopenharmony_ci#include <linux/capability.h> 1162306a36Sopenharmony_ci#include <linux/errno.h> 1262306a36Sopenharmony_ci#include <linux/sched.h> 1362306a36Sopenharmony_ci#include <linux/sched/user.h> 1462306a36Sopenharmony_ci#include <linux/mm.h> 1562306a36Sopenharmony_ci#include <linux/kernel.h> 1662306a36Sopenharmony_ci#include <linux/stat.h> 1762306a36Sopenharmony_ci#include <linux/socket.h> 1862306a36Sopenharmony_ci#include <linux/file.h> 1962306a36Sopenharmony_ci#include <linux/fcntl.h> 2062306a36Sopenharmony_ci#include <linux/net.h> 2162306a36Sopenharmony_ci#include <linux/interrupt.h> 2262306a36Sopenharmony_ci#include <linux/netdevice.h> 2362306a36Sopenharmony_ci#include <linux/security.h> 2462306a36Sopenharmony_ci#include <linux/pid_namespace.h> 2562306a36Sopenharmony_ci#include <linux/pid.h> 2662306a36Sopenharmony_ci#include <linux/nsproxy.h> 2762306a36Sopenharmony_ci#include <linux/slab.h> 2862306a36Sopenharmony_ci#include <linux/errqueue.h> 2962306a36Sopenharmony_ci#include <linux/io_uring.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include <linux/uaccess.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include <net/protocol.h> 3462306a36Sopenharmony_ci#include <linux/skbuff.h> 3562306a36Sopenharmony_ci#include <net/sock.h> 3662306a36Sopenharmony_ci#include <net/compat.h> 3762306a36Sopenharmony_ci#include <net/scm.h> 3862306a36Sopenharmony_ci#include <net/cls_cgroup.h> 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* 4262306a36Sopenharmony_ci * Only allow a user to send credentials, that they could set with 4362306a36Sopenharmony_ci * setu(g)id. 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic __inline__ int scm_check_creds(struct ucred *creds) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci const struct cred *cred = current_cred(); 4962306a36Sopenharmony_ci kuid_t uid = make_kuid(cred->user_ns, creds->uid); 5062306a36Sopenharmony_ci kgid_t gid = make_kgid(cred->user_ns, creds->gid); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci if (!uid_valid(uid) || !gid_valid(gid)) 5362306a36Sopenharmony_ci return -EINVAL; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci if ((creds->pid == task_tgid_vnr(current) || 5662306a36Sopenharmony_ci ns_capable(task_active_pid_ns(current)->user_ns, CAP_SYS_ADMIN)) && 5762306a36Sopenharmony_ci ((uid_eq(uid, cred->uid) || uid_eq(uid, cred->euid) || 5862306a36Sopenharmony_ci uid_eq(uid, cred->suid)) || ns_capable(cred->user_ns, CAP_SETUID)) && 5962306a36Sopenharmony_ci ((gid_eq(gid, cred->gid) || gid_eq(gid, cred->egid) || 6062306a36Sopenharmony_ci gid_eq(gid, cred->sgid)) || ns_capable(cred->user_ns, CAP_SETGID))) { 6162306a36Sopenharmony_ci return 0; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci return -EPERM; 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci int *fdp = (int*)CMSG_DATA(cmsg); 6962306a36Sopenharmony_ci struct scm_fp_list *fpl = *fplp; 7062306a36Sopenharmony_ci struct file **fpp; 7162306a36Sopenharmony_ci int i, num; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci num = (cmsg->cmsg_len - sizeof(struct cmsghdr))/sizeof(int); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (num <= 0) 7662306a36Sopenharmony_ci return 0; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (num > SCM_MAX_FD) 7962306a36Sopenharmony_ci return -EINVAL; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (!fpl) 8262306a36Sopenharmony_ci { 8362306a36Sopenharmony_ci fpl = kmalloc(sizeof(struct scm_fp_list), GFP_KERNEL_ACCOUNT); 8462306a36Sopenharmony_ci if (!fpl) 8562306a36Sopenharmony_ci return -ENOMEM; 8662306a36Sopenharmony_ci *fplp = fpl; 8762306a36Sopenharmony_ci fpl->count = 0; 8862306a36Sopenharmony_ci fpl->max = SCM_MAX_FD; 8962306a36Sopenharmony_ci fpl->user = NULL; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci fpp = &fpl->fp[fpl->count]; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (fpl->count + num > fpl->max) 9462306a36Sopenharmony_ci return -EINVAL; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci /* 9762306a36Sopenharmony_ci * Verify the descriptors and increment the usage count. 9862306a36Sopenharmony_ci */ 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci for (i=0; i< num; i++) 10162306a36Sopenharmony_ci { 10262306a36Sopenharmony_ci int fd = fdp[i]; 10362306a36Sopenharmony_ci struct file *file; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (fd < 0 || !(file = fget_raw(fd))) 10662306a36Sopenharmony_ci return -EBADF; 10762306a36Sopenharmony_ci /* don't allow io_uring files */ 10862306a36Sopenharmony_ci if (io_is_uring_fops(file)) { 10962306a36Sopenharmony_ci fput(file); 11062306a36Sopenharmony_ci return -EINVAL; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci *fpp++ = file; 11362306a36Sopenharmony_ci fpl->count++; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (!fpl->user) 11762306a36Sopenharmony_ci fpl->user = get_uid(current_user()); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci return num; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_civoid __scm_destroy(struct scm_cookie *scm) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct scm_fp_list *fpl = scm->fp; 12562306a36Sopenharmony_ci int i; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (fpl) { 12862306a36Sopenharmony_ci scm->fp = NULL; 12962306a36Sopenharmony_ci for (i=fpl->count-1; i>=0; i--) 13062306a36Sopenharmony_ci fput(fpl->fp[i]); 13162306a36Sopenharmony_ci free_uid(fpl->user); 13262306a36Sopenharmony_ci kfree(fpl); 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ciEXPORT_SYMBOL(__scm_destroy); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ciint __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci const struct proto_ops *ops = READ_ONCE(sock->ops); 14062306a36Sopenharmony_ci struct cmsghdr *cmsg; 14162306a36Sopenharmony_ci int err; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci for_each_cmsghdr(cmsg, msg) { 14462306a36Sopenharmony_ci err = -EINVAL; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* Verify that cmsg_len is at least sizeof(struct cmsghdr) */ 14762306a36Sopenharmony_ci /* The first check was omitted in <= 2.2.5. The reasoning was 14862306a36Sopenharmony_ci that parser checks cmsg_len in any case, so that 14962306a36Sopenharmony_ci additional check would be work duplication. 15062306a36Sopenharmony_ci But if cmsg_level is not SOL_SOCKET, we do not check 15162306a36Sopenharmony_ci for too short ancillary data object at all! Oops. 15262306a36Sopenharmony_ci OK, let's add it... 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_ci if (!CMSG_OK(msg, cmsg)) 15562306a36Sopenharmony_ci goto error; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (cmsg->cmsg_level != SOL_SOCKET) 15862306a36Sopenharmony_ci continue; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci switch (cmsg->cmsg_type) 16162306a36Sopenharmony_ci { 16262306a36Sopenharmony_ci case SCM_RIGHTS: 16362306a36Sopenharmony_ci if (!ops || ops->family != PF_UNIX) 16462306a36Sopenharmony_ci goto error; 16562306a36Sopenharmony_ci err=scm_fp_copy(cmsg, &p->fp); 16662306a36Sopenharmony_ci if (err<0) 16762306a36Sopenharmony_ci goto error; 16862306a36Sopenharmony_ci break; 16962306a36Sopenharmony_ci case SCM_CREDENTIALS: 17062306a36Sopenharmony_ci { 17162306a36Sopenharmony_ci struct ucred creds; 17262306a36Sopenharmony_ci kuid_t uid; 17362306a36Sopenharmony_ci kgid_t gid; 17462306a36Sopenharmony_ci if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct ucred))) 17562306a36Sopenharmony_ci goto error; 17662306a36Sopenharmony_ci memcpy(&creds, CMSG_DATA(cmsg), sizeof(struct ucred)); 17762306a36Sopenharmony_ci err = scm_check_creds(&creds); 17862306a36Sopenharmony_ci if (err) 17962306a36Sopenharmony_ci goto error; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci p->creds.pid = creds.pid; 18262306a36Sopenharmony_ci if (!p->pid || pid_vnr(p->pid) != creds.pid) { 18362306a36Sopenharmony_ci struct pid *pid; 18462306a36Sopenharmony_ci err = -ESRCH; 18562306a36Sopenharmony_ci pid = find_get_pid(creds.pid); 18662306a36Sopenharmony_ci if (!pid) 18762306a36Sopenharmony_ci goto error; 18862306a36Sopenharmony_ci put_pid(p->pid); 18962306a36Sopenharmony_ci p->pid = pid; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci err = -EINVAL; 19362306a36Sopenharmony_ci uid = make_kuid(current_user_ns(), creds.uid); 19462306a36Sopenharmony_ci gid = make_kgid(current_user_ns(), creds.gid); 19562306a36Sopenharmony_ci if (!uid_valid(uid) || !gid_valid(gid)) 19662306a36Sopenharmony_ci goto error; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci p->creds.uid = uid; 19962306a36Sopenharmony_ci p->creds.gid = gid; 20062306a36Sopenharmony_ci break; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci default: 20362306a36Sopenharmony_ci goto error; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (p->fp && !p->fp->count) 20862306a36Sopenharmony_ci { 20962306a36Sopenharmony_ci kfree(p->fp); 21062306a36Sopenharmony_ci p->fp = NULL; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci return 0; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cierror: 21562306a36Sopenharmony_ci scm_destroy(p); 21662306a36Sopenharmony_ci return err; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ciEXPORT_SYMBOL(__scm_send); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ciint put_cmsg(struct msghdr * msg, int level, int type, int len, void *data) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci int cmlen = CMSG_LEN(len); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (msg->msg_flags & MSG_CMSG_COMPAT) 22562306a36Sopenharmony_ci return put_cmsg_compat(msg, level, type, len, data); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (!msg->msg_control || msg->msg_controllen < sizeof(struct cmsghdr)) { 22862306a36Sopenharmony_ci msg->msg_flags |= MSG_CTRUNC; 22962306a36Sopenharmony_ci return 0; /* XXX: return error? check spec. */ 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci if (msg->msg_controllen < cmlen) { 23262306a36Sopenharmony_ci msg->msg_flags |= MSG_CTRUNC; 23362306a36Sopenharmony_ci cmlen = msg->msg_controllen; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci if (msg->msg_control_is_user) { 23762306a36Sopenharmony_ci struct cmsghdr __user *cm = msg->msg_control_user; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci check_object_size(data, cmlen - sizeof(*cm), true); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (!user_write_access_begin(cm, cmlen)) 24262306a36Sopenharmony_ci goto efault; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci unsafe_put_user(cmlen, &cm->cmsg_len, efault_end); 24562306a36Sopenharmony_ci unsafe_put_user(level, &cm->cmsg_level, efault_end); 24662306a36Sopenharmony_ci unsafe_put_user(type, &cm->cmsg_type, efault_end); 24762306a36Sopenharmony_ci unsafe_copy_to_user(CMSG_USER_DATA(cm), data, 24862306a36Sopenharmony_ci cmlen - sizeof(*cm), efault_end); 24962306a36Sopenharmony_ci user_write_access_end(); 25062306a36Sopenharmony_ci } else { 25162306a36Sopenharmony_ci struct cmsghdr *cm = msg->msg_control; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci cm->cmsg_level = level; 25462306a36Sopenharmony_ci cm->cmsg_type = type; 25562306a36Sopenharmony_ci cm->cmsg_len = cmlen; 25662306a36Sopenharmony_ci memcpy(CMSG_DATA(cm), data, cmlen - sizeof(*cm)); 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci cmlen = min(CMSG_SPACE(len), msg->msg_controllen); 26062306a36Sopenharmony_ci if (msg->msg_control_is_user) 26162306a36Sopenharmony_ci msg->msg_control_user += cmlen; 26262306a36Sopenharmony_ci else 26362306a36Sopenharmony_ci msg->msg_control += cmlen; 26462306a36Sopenharmony_ci msg->msg_controllen -= cmlen; 26562306a36Sopenharmony_ci return 0; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ciefault_end: 26862306a36Sopenharmony_ci user_write_access_end(); 26962306a36Sopenharmony_ciefault: 27062306a36Sopenharmony_ci return -EFAULT; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ciEXPORT_SYMBOL(put_cmsg); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_civoid put_cmsg_scm_timestamping64(struct msghdr *msg, struct scm_timestamping_internal *tss_internal) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci struct scm_timestamping64 tss; 27762306a36Sopenharmony_ci int i; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tss.ts); i++) { 28062306a36Sopenharmony_ci tss.ts[i].tv_sec = tss_internal->ts[i].tv_sec; 28162306a36Sopenharmony_ci tss.ts[i].tv_nsec = tss_internal->ts[i].tv_nsec; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPING_NEW, sizeof(tss), &tss); 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ciEXPORT_SYMBOL(put_cmsg_scm_timestamping64); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_civoid put_cmsg_scm_timestamping(struct msghdr *msg, struct scm_timestamping_internal *tss_internal) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci struct scm_timestamping tss; 29162306a36Sopenharmony_ci int i; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tss.ts); i++) { 29462306a36Sopenharmony_ci tss.ts[i].tv_sec = tss_internal->ts[i].tv_sec; 29562306a36Sopenharmony_ci tss.ts[i].tv_nsec = tss_internal->ts[i].tv_nsec; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci put_cmsg(msg, SOL_SOCKET, SO_TIMESTAMPING_OLD, sizeof(tss), &tss); 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ciEXPORT_SYMBOL(put_cmsg_scm_timestamping); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic int scm_max_fds(struct msghdr *msg) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci if (msg->msg_controllen <= sizeof(struct cmsghdr)) 30562306a36Sopenharmony_ci return 0; 30662306a36Sopenharmony_ci return (msg->msg_controllen - sizeof(struct cmsghdr)) / sizeof(int); 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_civoid scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct cmsghdr __user *cm = 31262306a36Sopenharmony_ci (__force struct cmsghdr __user *)msg->msg_control_user; 31362306a36Sopenharmony_ci unsigned int o_flags = (msg->msg_flags & MSG_CMSG_CLOEXEC) ? O_CLOEXEC : 0; 31462306a36Sopenharmony_ci int fdmax = min_t(int, scm_max_fds(msg), scm->fp->count); 31562306a36Sopenharmony_ci int __user *cmsg_data = CMSG_USER_DATA(cm); 31662306a36Sopenharmony_ci int err = 0, i; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* no use for FD passing from kernel space callers */ 31962306a36Sopenharmony_ci if (WARN_ON_ONCE(!msg->msg_control_is_user)) 32062306a36Sopenharmony_ci return; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci if (msg->msg_flags & MSG_CMSG_COMPAT) { 32362306a36Sopenharmony_ci scm_detach_fds_compat(msg, scm); 32462306a36Sopenharmony_ci return; 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci for (i = 0; i < fdmax; i++) { 32862306a36Sopenharmony_ci err = receive_fd_user(scm->fp->fp[i], cmsg_data + i, o_flags); 32962306a36Sopenharmony_ci if (err < 0) 33062306a36Sopenharmony_ci break; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (i > 0) { 33462306a36Sopenharmony_ci int cmlen = CMSG_LEN(i * sizeof(int)); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci err = put_user(SOL_SOCKET, &cm->cmsg_level); 33762306a36Sopenharmony_ci if (!err) 33862306a36Sopenharmony_ci err = put_user(SCM_RIGHTS, &cm->cmsg_type); 33962306a36Sopenharmony_ci if (!err) 34062306a36Sopenharmony_ci err = put_user(cmlen, &cm->cmsg_len); 34162306a36Sopenharmony_ci if (!err) { 34262306a36Sopenharmony_ci cmlen = CMSG_SPACE(i * sizeof(int)); 34362306a36Sopenharmony_ci if (msg->msg_controllen < cmlen) 34462306a36Sopenharmony_ci cmlen = msg->msg_controllen; 34562306a36Sopenharmony_ci msg->msg_control_user += cmlen; 34662306a36Sopenharmony_ci msg->msg_controllen -= cmlen; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (i < scm->fp->count || (scm->fp->count && fdmax <= 0)) 35162306a36Sopenharmony_ci msg->msg_flags |= MSG_CTRUNC; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci /* 35462306a36Sopenharmony_ci * All of the files that fit in the message have had their usage counts 35562306a36Sopenharmony_ci * incremented, so we just free the list. 35662306a36Sopenharmony_ci */ 35762306a36Sopenharmony_ci __scm_destroy(scm); 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ciEXPORT_SYMBOL(scm_detach_fds); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistruct scm_fp_list *scm_fp_dup(struct scm_fp_list *fpl) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci struct scm_fp_list *new_fpl; 36462306a36Sopenharmony_ci int i; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (!fpl) 36762306a36Sopenharmony_ci return NULL; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci new_fpl = kmemdup(fpl, offsetof(struct scm_fp_list, fp[fpl->count]), 37062306a36Sopenharmony_ci GFP_KERNEL_ACCOUNT); 37162306a36Sopenharmony_ci if (new_fpl) { 37262306a36Sopenharmony_ci for (i = 0; i < fpl->count; i++) 37362306a36Sopenharmony_ci get_file(fpl->fp[i]); 37462306a36Sopenharmony_ci new_fpl->max = new_fpl->count; 37562306a36Sopenharmony_ci new_fpl->user = get_uid(fpl->user); 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci return new_fpl; 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ciEXPORT_SYMBOL(scm_fp_dup); 380