summaryrefslogtreecommitdiff
path: root/ipc/msg.c
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/msg.c')
-rw-r--r--ipc/msg.c64
1 files changed, 62 insertions, 2 deletions
diff --git a/ipc/msg.c b/ipc/msg.c
index cefc24f46e3e..d20ffc7d3f24 100644
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -769,6 +769,45 @@ static long do_msg_fill(void __user *dest, struct msg_msg *msg, size_t bufsz)
return msgsz;
}
+#ifdef CONFIG_CHECKPOINT_RESTORE
+static inline struct msg_msg *fill_copy(unsigned long copy_nr,
+ unsigned long msg_nr,
+ struct msg_msg *msg,
+ struct msg_msg *copy)
+{
+ if (copy_nr == msg_nr)
+ return copy_msg(msg, copy);
+ return NULL;
+}
+
+static inline struct msg_msg *prepare_copy(void __user *buf, size_t bufsz,
+ int msgflg, long *msgtyp,
+ unsigned long *copy_number)
+{
+ struct msg_msg *copy;
+
+ *copy_number = *msgtyp;
+ *msgtyp = 0;
+ /*
+ * Create dummy message to copy real message to.
+ */
+ copy = load_msg(buf, bufsz);
+ if (!IS_ERR(copy))
+ copy->m_ts = bufsz;
+ return copy;
+}
+
+static inline void free_copy(int msgflg, struct msg_msg *copy)
+{
+ if (msgflg & MSG_COPY)
+ free_msg(copy);
+}
+#else
+#define free_copy(msgflg, copy) do {} while (0)
+#define prepare_copy(buf, sz, msgflg, msgtyp, copy_nr) ERR_PTR(-ENOSYS)
+#define fill_copy(copy_nr, msg_nr, msg, copy) NULL
+#endif
+
long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
int msgflg,
long (*msg_handler)(void __user *, struct msg_msg *, size_t))
@@ -777,19 +816,29 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
struct msg_msg *msg;
int mode;
struct ipc_namespace *ns;
+ struct msg_msg *copy;
+ unsigned long __maybe_unused copy_number;
if (msqid < 0 || (long) bufsz < 0)
return -EINVAL;
+ if (msgflg & MSG_COPY) {
+ copy = prepare_copy(buf, bufsz, msgflg, &msgtyp, &copy_number);
+ if (IS_ERR(copy))
+ return PTR_ERR(copy);
+ }
mode = convert_mode(&msgtyp, msgflg);
ns = current->nsproxy->ipc_ns;
msq = msg_lock_check(ns, msqid);
- if (IS_ERR(msq))
+ if (IS_ERR(msq)) {
+ free_copy(msgflg, copy);
return PTR_ERR(msq);
+ }
for (;;) {
struct msg_receiver msr_d;
struct list_head *tmp;
+ long msg_counter = 0;
msg = ERR_PTR(-EACCES);
if (ipcperms(ns, &msq->q_perm, S_IRUGO))
@@ -809,8 +858,15 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
if (mode == SEARCH_LESSEQUAL &&
walk_msg->m_type != 1) {
msgtyp = walk_msg->m_type - 1;
+ } else if (msgflg & MSG_COPY) {
+ msg = fill_copy(copy_number,
+ msg_counter,
+ walk_msg, copy);
+ if (msg)
+ break;
} else
break;
+ msg_counter++;
}
tmp = tmp->next;
}
@@ -823,6 +879,8 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp,
msg = ERR_PTR(-E2BIG);
goto out_unlock;
}
+ if (msgflg & MSG_COPY)
+ goto out_unlock;
list_del(&msg->m_list);
msq->q_qnum--;
msq->q_rtime = get_seconds();
@@ -906,8 +964,10 @@ out_unlock:
break;
}
}
- if (IS_ERR(msg))
+ if (IS_ERR(msg)) {
+ free_copy(msgflg, copy);
return PTR_ERR(msg);
+ }
bufsz = msg_handler(buf, msg, bufsz);
free_msg(msg);