[RFC][PATCH 1/2] System V IPC: new kernel API to change an ID

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




From: Pierre Peiffer <[email protected]>

This patch provides three new API for changing the ID of an existing
System V IPCs.

These APIs are:
	long msg_mvid(struct ipc_namespace *ns, int id, int newid);
	long sem_mvid(struct ipc_namespace *ns, int id, int newid);
	long shm_mvid(struct ipc_namespace *ns, int id, int newid);

They return 0 or an error code in case of failure.

They may be useful for setting a specific ID for an IPC when preparing
a restart operation.

To be successful, the following rules must be respected:
- the IPC exists (of course...)
- the new ID must satisfy the ID computation rule.
- the entry (in the kernel internal table of IPCs) corresponding to the new
  ID must be free.

Signed-off-by: Pierre Peiffer <[email protected]>
---

 include/linux/msg.h |    1 +
 include/linux/sem.h |    1 +
 include/linux/shm.h |    1 +
 ipc/msg.c           |   32 +++++++++++++++++++++++++++
 ipc/sem.c           |   32 +++++++++++++++++++++++++++
 ipc/shm.c           |   30 ++++++++++++++++++++++++++
 ipc/util.c          |   60 +++++++++++++++++++++++++++++++++++++++++++++++++++
 ipc/util.h          |    1 +
 8 files changed, 158 insertions(+), 0 deletions(-)

diff --git a/include/linux/msg.h b/include/linux/msg.h
index f1b6074..5a1db95 100644
--- a/include/linux/msg.h
+++ b/include/linux/msg.h
@@ -97,6 +97,7 @@ extern long do_msgsnd(int msqid, long mtype, void __user *mtext,
 			size_t msgsz, int msgflg);
 extern long do_msgrcv(int msqid, long *pmtype, void __user *mtext,
 			size_t msgsz, long msgtyp, int msgflg);
+long msg_mvid(struct ipc_namespace *ns, int id, int newid);
 
 #endif /* __KERNEL__ */
 
diff --git a/include/linux/sem.h b/include/linux/sem.h
index 9aaffb0..b5989fb 100644
--- a/include/linux/sem.h
+++ b/include/linux/sem.h
@@ -142,6 +142,7 @@ struct sysv_sem {
 
 extern int copy_semundo(unsigned long clone_flags, struct task_struct *tsk);
 extern void exit_sem(struct task_struct *tsk);
+long sem_mvid(struct ipc_namespace *ns, int id, int newid);
 
 #else
 static inline int copy_semundo(unsigned long clone_flags, struct task_struct *tsk)
diff --git a/include/linux/shm.h b/include/linux/shm.h
index ad2e3af..f4ae995 100644
--- a/include/linux/shm.h
+++ b/include/linux/shm.h
@@ -97,6 +97,7 @@ struct shmid_kernel /* private to the kernel */
 #ifdef CONFIG_SYSVIPC
 long do_shmat(int shmid, char __user *shmaddr, int shmflg, unsigned long *addr);
 extern int is_file_shm_hugepages(struct file *file);
+long shm_mvid(struct ipc_namespace *ns, int id, int newid);
 #else
 static inline long do_shmat(int shmid, char __user *shmaddr,
 				int shmflg, unsigned long *addr)
diff --git a/ipc/msg.c b/ipc/msg.c
index a03fcb5..d9d4093 100644
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -382,6 +382,38 @@ copy_msqid_from_user(struct msq_setbuf *out, void __user *buf, int version)
 	}
 }
 
+long msg_mvid(struct ipc_namespace *ns, int id, int newid)
+{
+	long err;
+	struct msg_queue *msq;
+
+	mutex_lock(&msg_ids(ns).mutex);
+	msq = msg_lock(ns, id);
+
+	err = -EINVAL;
+	if (msq == NULL)
+			goto out_up;
+
+	err = msg_checkid(ns, msq, id);
+	if (err)
+		goto out_unlock_up;
+
+	err = ipc_mvid(&msg_ids(ns), id,
+		       newid, ns->msg_ctlmni);
+
+	if (err)
+		goto out_unlock_up;
+
+	msq->q_id = newid;
+	msq->q_ctime = get_seconds();
+
+out_unlock_up:
+	msg_unlock(msq);
+out_up:
+	mutex_unlock(&msg_ids(ns).mutex);
+	return err;
+}
+
 asmlinkage long sys_msgctl(int msqid, int cmd, struct msqid_ds __user *buf)
 {
 	struct kern_ipc_perm *ipcp;
diff --git a/ipc/sem.c b/ipc/sem.c
index b676fef..606f2e9 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -918,6 +918,38 @@ out_unlock:
 	return err;
 }
 
+long sem_mvid(struct ipc_namespace *ns, int id, int newid)
+{
+	long err;
+	struct sem_array *sma;
+
+	mutex_lock(&sem_ids(ns).mutex);
+	sma = sem_lock(ns, id);
+
+	err = -EINVAL;
+	if (sma == NULL)
+			goto out_up;
+
+	err = sem_checkid(ns, sma, id);
+	if (err)
+		goto out_unlock_up;
+
+	err = ipc_mvid(&sem_ids(ns), id,
+		       newid, ns->sc_semmni);
+
+	if (err)
+		goto out_unlock_up;
+
+	sma->sem_id = newid;
+	sma->sem_ctime = get_seconds();
+
+out_unlock_up:
+	sem_unlock(sma);
+out_up:
+	mutex_unlock(&sem_ids(ns).mutex);
+	return err;
+}
+
 asmlinkage long sys_semctl (int semid, int semnum, int cmd, union semun arg)
 {
 	int err = -EINVAL;
diff --git a/ipc/shm.c b/ipc/shm.c
index a86a3a5..5f4bca6 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -156,7 +156,37 @@ static inline int shm_addid(struct ipc_namespace *ns, struct shmid_kernel *shp)
 	return ipc_addid(&shm_ids(ns), &shp->shm_perm, ns->shm_ctlmni);
 }
 
+long shm_mvid(struct ipc_namespace *ns, int id, int newid)
+{
+	long err;
+	struct shmid_kernel *shp;
+
+	mutex_lock(&shm_ids(ns).mutex);
+	shp = shm_lock(ns, id);
+
+	err = -EINVAL;
+	if (shp == NULL)
+			goto out_up;
+
+	err = shm_checkid(ns, shp, id);
+	if (err)
+		goto out_unlock_up;
+
+	err = ipc_mvid(&shm_ids(ns), id,
+		       newid, ns->shm_ctlmni);
 
+	if (err)
+		goto out_unlock_up;
+
+	shp->id = newid;
+	shp->shm_ctim = get_seconds();
+
+out_unlock_up:
+	shm_unlock(shp);
+out_up:
+	mutex_unlock(&shm_ids(ns).mutex);
+	return err;
+}
 
 /* This is called by fork, once for every shm attach. */
 static void shm_open(struct vm_area_struct *vma)
diff --git a/ipc/util.c b/ipc/util.c
index 44e5135..4f338d4 100644
--- a/ipc/util.c
+++ b/ipc/util.c
@@ -318,6 +318,66 @@ found:
 }
 
 /**
+ *	ipc_mvid 	-	move an IPC identifier
+ *	@ids: IPC identifier set
+ *	@oldid: ID of the IPC permission set to move
+ *	@newid: new ID of the IPC permission set to move
+ *	@size: new size limit for the id array
+ *
+ *	Move an entry in the IPC arrays from the 'oldid' place to the
+ *      'newid' place. The seq number of the entry is updated to match the
+ *      'newid' value.
+ *
+ *	Called with the list lock and ipc_ids.mutex held.
+ */
+
+int ipc_mvid(struct ipc_ids *ids, int oldid, int newid, int size)
+{
+	struct kern_ipc_perm *p;
+	int old_lid = oldid % SEQ_MULTIPLIER;
+	int new_lid = newid % SEQ_MULTIPLIER;
+
+	if ((new_lid >= size) ||
+	    newid !=  (new_lid + (newid/SEQ_MULTIPLIER)*SEQ_MULTIPLIER))
+		return -ERANGE;
+
+	size = grow_ary(ids, size);
+
+	BUG_ON(old_lid >= ids->entries->size);
+
+	p = ids->entries->p[old_lid];
+
+	if (!p)
+		return -ENXIO;
+
+	/*
+	 * The id (n° of the entry in the table entries) may be the same
+	 * but not the seq number.
+	 */
+	if (new_lid != old_lid) {
+
+		if (ids->entries->p[new_lid])
+			return -EBUSY;
+
+		ids->entries->p[new_lid] = p;
+
+		ids->entries->p[old_lid] = NULL;
+
+		if (new_lid > ids->max_id)
+			ids->max_id = new_lid;
+		if (old_lid == ids->max_id) {
+			do {
+				--old_lid;
+			} while (ids->entries->p[old_lid] == NULL);
+			ids->max_id = old_lid;
+		}
+	}
+
+	p->seq = newid/SEQ_MULTIPLIER;
+	return 0;
+}
+
+/**
  *	ipc_rmid	-	remove an IPC identifier
  *	@ids: identifier set
  *	@id: Identifier to remove
diff --git a/ipc/util.h b/ipc/util.h
index 333e891..886af31 100644
--- a/ipc/util.h
+++ b/ipc/util.h
@@ -59,6 +59,7 @@ int ipc_findkey(struct ipc_ids* ids, key_t key);
 int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size);
 
 /* must be called with both locks acquired. */
+int ipc_mvid(struct ipc_ids *ids, int oldid, int newid, int size);
 struct kern_ipc_perm* ipc_rmid(struct ipc_ids* ids, int id);
 
 int ipcperms (struct kern_ipc_perm *ipcp, short flg);


-- 
Pierre
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux