File: /usr/src/linux/ipc/shm.c

1     /*
2      * linux/ipc/shm.c
3      * Copyright (C) 1992, 1993 Krishna Balasubramanian
4      *	 Many improvements/fixes by Bruno Haible.
5      * Replaced `struct shm_desc' by `struct vm_area_struct', July 1994.
6      * Fixed the shm swap deallocation (shm_unuse()), August 1998 Andrea Arcangeli.
7      *
8      * /proc/sysvipc/shm support (c) 1999 Dragos Acostachioaie <dragos@iname.com>
9      * BIGMEM support, Andrea Arcangeli <andrea@suse.de>
10      * SMP thread shm, Jean-Luc Boyard <jean-luc.boyard@siemens.fr>
11      * HIGHMEM support, Ingo Molnar <mingo@redhat.com>
12      * Make shmmax, shmall, shmmni sysctl'able, Christoph Rohland <cr@sap.com>
13      * Shared /dev/zero support, Kanoj Sarcar <kanoj@sgi.com>
14      * Move the mm functionality over to mm/shmem.c, Christoph Rohland <cr@sap.com>
15      *
16      */
17     
18     #include <linux/config.h>
19     #include <linux/slab.h>
20     #include <linux/shm.h>
21     #include <linux/init.h>
22     #include <linux/file.h>
23     #include <linux/mman.h>
24     #include <linux/proc_fs.h>
25     #include <asm/uaccess.h>
26     
27     #include "util.h"
28     
29     struct shmid_kernel /* private to the kernel */
30     {	
31     	struct kern_ipc_perm	shm_perm;
32     	struct file *		shm_file;
33     	int			id;
34     	unsigned long		shm_nattch;
35     	unsigned long		shm_segsz;
36     	time_t			shm_atim;
37     	time_t			shm_dtim;
38     	time_t			shm_ctim;
39     	pid_t			shm_cprid;
40     	pid_t			shm_lprid;
41     };
42     
43     #define shm_flags	shm_perm.mode
44     
45     static struct file_operations shm_file_operations;
46     static struct vm_operations_struct shm_vm_ops;
47     
48     static struct ipc_ids shm_ids;
49     
50     #define shm_lock(id)	((struct shmid_kernel*)ipc_lock(&shm_ids,id))
51     #define shm_unlock(id)	ipc_unlock(&shm_ids,id)
52     #define shm_lockall()	ipc_lockall(&shm_ids)
53     #define shm_unlockall()	ipc_unlockall(&shm_ids)
54     #define shm_get(id)	((struct shmid_kernel*)ipc_get(&shm_ids,id))
55     #define shm_buildid(id, seq) \
56     	ipc_buildid(&shm_ids, id, seq)
57     
58     static int newseg (key_t key, int shmflg, size_t size);
59     static void shm_open (struct vm_area_struct *shmd);
60     static void shm_close (struct vm_area_struct *shmd);
61     #ifdef CONFIG_PROC_FS
62     static int sysvipc_shm_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data);
63     #endif
64     
65     size_t	shm_ctlmax = SHMMAX;
66     size_t 	shm_ctlall = SHMALL;
67     int 	shm_ctlmni = SHMMNI;
68     
69     static int shm_tot; /* total number of shared memory pages */
70     
71     void __init shm_init (void)
72     {
73     	ipc_init_ids(&shm_ids, 1);
74     #ifdef CONFIG_PROC_FS
75     	create_proc_read_entry("sysvipc/shm", 0, 0, sysvipc_shm_read_proc, NULL);
76     #endif
77     }
78     
79     static inline int shm_checkid(struct shmid_kernel *s, int id)
80     {
81     	if (ipc_checkid(&shm_ids,&s->shm_perm,id))
82     		return -EIDRM;
83     	return 0;
84     }
85     
86     static inline struct shmid_kernel *shm_rmid(int id)
87     {
88     	return (struct shmid_kernel *)ipc_rmid(&shm_ids,id);
89     }
90     
91     static inline int shm_addid(struct shmid_kernel *shp)
92     {
93     	return ipc_addid(&shm_ids, &shp->shm_perm, shm_ctlmni+1);
94     }
95     
96     
97     
98     static inline void shm_inc (int id) {
99     	struct shmid_kernel *shp;
100     
101     	if(!(shp = shm_lock(id)))
102     		BUG();
103     	shp->shm_atim = CURRENT_TIME;
104     	shp->shm_lprid = current->pid;
105     	shp->shm_nattch++;
106     	shm_unlock(id);
107     }
108     
109     /* This is called by fork, once for every shm attach. */
110     static void shm_open (struct vm_area_struct *shmd)
111     {
112     	shm_inc (shmd->vm_file->f_dentry->d_inode->i_ino);
113     }
114     
115     /*
116      * shm_destroy - free the struct shmid_kernel
117      *
118      * @shp: struct to free
119      *
120      * It has to be called with shp and shm_ids.sem locked
121      */
122     static void shm_destroy (struct shmid_kernel *shp)
123     {
124     	shm_tot -= (shp->shm_segsz + PAGE_SIZE - 1) >> PAGE_SHIFT;
125     	shm_rmid (shp->id);
126     	shmem_lock(shp->shm_file, 0);
127     	fput (shp->shm_file);
128     	kfree (shp);
129     }
130     
131     /*
132      * remove the attach descriptor shmd.
133      * free memory for segment if it is marked destroyed.
134      * The descriptor has already been removed from the current->mm->mmap list
135      * and will later be kfree()d.
136      */
137     static void shm_close (struct vm_area_struct *shmd)
138     {
139     	struct file * file = shmd->vm_file;
140     	int id = file->f_dentry->d_inode->i_ino;
141     	struct shmid_kernel *shp;
142     
143     	down (&shm_ids.sem);
144     	/* remove from the list of attaches of the shm segment */
145     	if(!(shp = shm_lock(id)))
146     		BUG();
147     	shp->shm_lprid = current->pid;
148     	shp->shm_dtim = CURRENT_TIME;
149     	shp->shm_nattch--;
150     	if(shp->shm_nattch == 0 &&
151     	   shp->shm_flags & SHM_DEST)
152     		shm_destroy (shp);
153     
154     	shm_unlock(id);
155     	up (&shm_ids.sem);
156     }
157     
158     static int shm_mmap(struct file * file, struct vm_area_struct * vma)
159     {
160     	UPDATE_ATIME(file->f_dentry->d_inode);
161     	vma->vm_ops = &shm_vm_ops;
162     	shm_inc(file->f_dentry->d_inode->i_ino);
163     	return 0;
164     }
165     
166     static struct file_operations shm_file_operations = {
167     	mmap:	shm_mmap
168     };
169     
170     static struct vm_operations_struct shm_vm_ops = {
171     	open:	shm_open,	/* callback for a new vm-area open */
172     	close:	shm_close,	/* callback for when the vm-area is released */
173     	nopage:	shmem_nopage,
174     };
175     
176     static int newseg (key_t key, int shmflg, size_t size)
177     {
178     	int error;
179     	struct shmid_kernel *shp;
180     	int numpages = (size + PAGE_SIZE -1) >> PAGE_SHIFT;
181     	struct file * file;
182     	char name[13];
183     	int id;
184     
185     	if (size < SHMMIN || size > shm_ctlmax)
186     		return -EINVAL;
187     
188     	if (shm_tot + numpages >= shm_ctlall)
189     		return -ENOSPC;
190     
191     	shp = (struct shmid_kernel *) kmalloc (sizeof (*shp), GFP_USER);
192     	if (!shp)
193     		return -ENOMEM;
194     	sprintf (name, "SYSV%08x", key);
195     	file = shmem_file_setup(name, size);
196     	error = PTR_ERR(file);
197     	if (IS_ERR(file))
198     		goto no_file;
199     
200     	error = -ENOSPC;
201     	id = shm_addid(shp);
202     	if(id == -1) 
203     		goto no_id;
204     	shp->shm_perm.key = key;
205     	shp->shm_flags = (shmflg & S_IRWXUGO);
206     	shp->shm_cprid = current->pid;
207     	shp->shm_lprid = 0;
208     	shp->shm_atim = shp->shm_dtim = 0;
209     	shp->shm_ctim = CURRENT_TIME;
210     	shp->shm_segsz = size;
211     	shp->shm_nattch = 0;
212     	shp->id = shm_buildid(id,shp->shm_perm.seq);
213     	shp->shm_file = file;
214     	file->f_dentry->d_inode->i_ino = shp->id;
215     	file->f_op = &shm_file_operations;
216     	shm_tot += numpages;
217     	shm_unlock (id);
218     	return shp->id;
219     
220     no_id:
221     	fput(file);
222     no_file:
223     	kfree(shp);
224     	return error;
225     }
226     
227     asmlinkage long sys_shmget (key_t key, size_t size, int shmflg)
228     {
229     	struct shmid_kernel *shp;
230     	int err, id = 0;
231     
232     	down(&shm_ids.sem);
233     	if (key == IPC_PRIVATE) {
234     		err = newseg(key, shmflg, size);
235     	} else if ((id = ipc_findkey(&shm_ids, key)) == -1) {
236     		if (!(shmflg & IPC_CREAT))
237     			err = -ENOENT;
238     		else
239     			err = newseg(key, shmflg, size);
240     	} else if ((shmflg & IPC_CREAT) && (shmflg & IPC_EXCL)) {
241     		err = -EEXIST;
242     	} else {
243     		shp = shm_lock(id);
244     		if(shp==NULL)
245     			BUG();
246     		if (shp->shm_segsz < size)
247     			err = -EINVAL;
248     		else if (ipcperms(&shp->shm_perm, shmflg))
249     			err = -EACCES;
250     		else
251     			err = shm_buildid(id, shp->shm_perm.seq);
252     		shm_unlock(id);
253     	}
254     	up(&shm_ids.sem);
255     	return err;
256     }
257     
258     static inline unsigned long copy_shmid_to_user(void *buf, struct shmid64_ds *in, int version)
259     {
260     	switch(version) {
261     	case IPC_64:
262     		return copy_to_user(buf, in, sizeof(*in));
263     	case IPC_OLD:
264     	    {
265     		struct shmid_ds out;
266     
267     		ipc64_perm_to_ipc_perm(&in->shm_perm, &out.shm_perm);
268     		out.shm_segsz	= in->shm_segsz;
269     		out.shm_atime	= in->shm_atime;
270     		out.shm_dtime	= in->shm_dtime;
271     		out.shm_ctime	= in->shm_ctime;
272     		out.shm_cpid	= in->shm_cpid;
273     		out.shm_lpid	= in->shm_lpid;
274     		out.shm_nattch	= in->shm_nattch;
275     
276     		return copy_to_user(buf, &out, sizeof(out));
277     	    }
278     	default:
279     		return -EINVAL;
280     	}
281     }
282     
283     struct shm_setbuf {
284     	uid_t	uid;
285     	gid_t	gid;
286     	mode_t	mode;
287     };	
288     
289     static inline unsigned long copy_shmid_from_user(struct shm_setbuf *out, void *buf, int version)
290     {
291     	switch(version) {
292     	case IPC_64:
293     	    {
294     		struct shmid64_ds tbuf;
295     
296     		if (copy_from_user(&tbuf, buf, sizeof(tbuf)))
297     			return -EFAULT;
298     
299     		out->uid	= tbuf.shm_perm.uid;
300     		out->gid	= tbuf.shm_perm.gid;
301     		out->mode	= tbuf.shm_flags;
302     
303     		return 0;
304     	    }
305     	case IPC_OLD:
306     	    {
307     		struct shmid_ds tbuf_old;
308     
309     		if (copy_from_user(&tbuf_old, buf, sizeof(tbuf_old)))
310     			return -EFAULT;
311     
312     		out->uid	= tbuf_old.shm_perm.uid;
313     		out->gid	= tbuf_old.shm_perm.gid;
314     		out->mode	= tbuf_old.shm_flags;
315     
316     		return 0;
317     	    }
318     	default:
319     		return -EINVAL;
320     	}
321     }
322     
323     static inline unsigned long copy_shminfo_to_user(void *buf, struct shminfo64 *in, int version)
324     {
325     	switch(version) {
326     	case IPC_64:
327     		return copy_to_user(buf, in, sizeof(*in));
328     	case IPC_OLD:
329     	    {
330     		struct shminfo out;
331     
332     		if(in->shmmax > INT_MAX)
333     			out.shmmax = INT_MAX;
334     		else
335     			out.shmmax = (int)in->shmmax;
336     
337     		out.shmmin	= in->shmmin;
338     		out.shmmni	= in->shmmni;
339     		out.shmseg	= in->shmseg;
340     		out.shmall	= in->shmall; 
341     
342     		return copy_to_user(buf, &out, sizeof(out));
343     	    }
344     	default:
345     		return -EINVAL;
346     	}
347     }
348     
349     static void shm_get_stat (unsigned long *rss, unsigned long *swp) 
350     {
351     	int i;
352     
353     	*rss = 0;
354     	*swp = 0;
355     
356     	for(i = 0; i <= shm_ids.max_id; i++) {
357     		struct shmid_kernel* shp;
358     		struct inode * inode;
359     
360     		shp = shm_get(i);
361     		if(shp == NULL)
362     			continue;
363     		inode = shp->shm_file->f_dentry->d_inode;
364     		spin_lock (&inode->u.shmem_i.lock);
365     		*rss += inode->i_mapping->nrpages;
366     		*swp += inode->u.shmem_i.swapped;
367     		spin_unlock (&inode->u.shmem_i.lock);
368     	}
369     }
370     
371     asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds *buf)
372     {
373     	struct shm_setbuf setbuf;
374     	struct shmid_kernel *shp;
375     	int err, version;
376     
377     	if (cmd < 0 || shmid < 0)
378     		return -EINVAL;
379     
380     	version = ipc_parse_version(&cmd);
381     
382     	switch (cmd) { /* replace with proc interface ? */
383     	case IPC_INFO:
384     	{
385     		struct shminfo64 shminfo;
386     
387     		memset(&shminfo,0,sizeof(shminfo));
388     		shminfo.shmmni = shminfo.shmseg = shm_ctlmni;
389     		shminfo.shmmax = shm_ctlmax;
390     		shminfo.shmall = shm_ctlall;
391     
392     		shminfo.shmmin = SHMMIN;
393     		if(copy_shminfo_to_user (buf, &shminfo, version))
394     			return -EFAULT;
395     		/* reading a integer is always atomic */
396     		err= shm_ids.max_id;
397     		if(err<0)
398     			err = 0;
399     		return err;
400     	}
401     	case SHM_INFO:
402     	{
403     		struct shm_info shm_info;
404     
405     		memset(&shm_info,0,sizeof(shm_info));
406     		down(&shm_ids.sem);
407     		shm_lockall();
408     		shm_info.used_ids = shm_ids.in_use;
409     		shm_get_stat (&shm_info.shm_rss, &shm_info.shm_swp);
410     		shm_info.shm_tot = shm_tot;
411     		shm_info.swap_attempts = 0;
412     		shm_info.swap_successes = 0;
413     		err = shm_ids.max_id;
414     		shm_unlockall();
415     		up(&shm_ids.sem);
416     		if(copy_to_user (buf, &shm_info, sizeof(shm_info)))
417     			return -EFAULT;
418     
419     		return err < 0 ? 0 : err;
420     	}
421     	case SHM_STAT:
422     	case IPC_STAT:
423     	{
424     		struct shmid64_ds tbuf;
425     		int result;
426     		memset(&tbuf, 0, sizeof(tbuf));
427     		shp = shm_lock(shmid);
428     		if(shp==NULL)
429     			return -EINVAL;
430     		if(cmd==SHM_STAT) {
431     			err = -EINVAL;
432     			if (shmid > shm_ids.max_id)
433     				goto out_unlock;
434     			result = shm_buildid(shmid, shp->shm_perm.seq);
435     		} else {
436     			err = shm_checkid(shp,shmid);
437     			if(err)
438     				goto out_unlock;
439     			result = 0;
440     		}
441     		err=-EACCES;
442     		if (ipcperms (&shp->shm_perm, S_IRUGO))
443     			goto out_unlock;
444     		kernel_to_ipc64_perm(&shp->shm_perm, &tbuf.shm_perm);
445     		tbuf.shm_segsz	= shp->shm_segsz;
446     		tbuf.shm_atime	= shp->shm_atim;
447     		tbuf.shm_dtime	= shp->shm_dtim;
448     		tbuf.shm_ctime	= shp->shm_ctim;
449     		tbuf.shm_cpid	= shp->shm_cprid;
450     		tbuf.shm_lpid	= shp->shm_lprid;
451     		tbuf.shm_nattch	= shp->shm_nattch;
452     		shm_unlock(shmid);
453     		if(copy_shmid_to_user (buf, &tbuf, version))
454     			return -EFAULT;
455     		return result;
456     	}
457     	case SHM_LOCK:
458     	case SHM_UNLOCK:
459     	{
460     /* Allow superuser to lock segment in memory */
461     /* Should the pages be faulted in here or leave it to user? */
462     /* need to determine interaction with current->swappable */
463     		if (!capable(CAP_IPC_LOCK))
464     			return -EPERM;
465     
466     		shp = shm_lock(shmid);
467     		if(shp==NULL)
468     			return -EINVAL;
469     		err = shm_checkid(shp,shmid);
470     		if(err)
471     			goto out_unlock;
472     		if(cmd==SHM_LOCK) {
473     			shmem_lock(shp->shm_file, 1);
474     			shp->shm_flags |= SHM_LOCKED;
475     		} else {
476     			shmem_lock(shp->shm_file, 0);
477     			shp->shm_flags &= ~SHM_LOCKED;
478     		}
479     		shm_unlock(shmid);
480     		return err;
481     	}
482     	case IPC_RMID:
483     	{
484     		/*
485     		 *	We cannot simply remove the file. The SVID states
486     		 *	that the block remains until the last person
487     		 *	detaches from it, then is deleted. A shmat() on
488     		 *	an RMID segment is legal in older Linux and if 
489     		 *	we change it apps break...
490     		 *
491     		 *	Instead we set a destroyed flag, and then blow
492     		 *	the name away when the usage hits zero.
493     		 */
494     		down(&shm_ids.sem);
495     		shp = shm_lock(shmid);
496     		err = -EINVAL;
497     		if (shp == NULL) 
498     			goto out_up;
499     		err = shm_checkid(shp, shmid);
500     		if(err)
501     			goto out_unlock_up;
502     		if (current->euid != shp->shm_perm.uid &&
503     		    current->euid != shp->shm_perm.cuid && 
504     		    !capable(CAP_SYS_ADMIN)) {
505     			err=-EPERM;
506     			goto out_unlock_up;
507     		}
508     		if (shp->shm_nattch){
509     			shp->shm_flags |= SHM_DEST;
510     			/* Do not find it any more */
511     			shp->shm_perm.key = IPC_PRIVATE;
512     		} else
513     			shm_destroy (shp);
514     
515     		/* Unlock */
516     		shm_unlock(shmid);
517     		up(&shm_ids.sem);
518     		return err;
519     	}
520     
521     	case IPC_SET:
522     	{
523     		if(copy_shmid_from_user (&setbuf, buf, version))
524     			return -EFAULT;
525     		down(&shm_ids.sem);
526     		shp = shm_lock(shmid);
527     		err=-EINVAL;
528     		if(shp==NULL)
529     			goto out_up;
530     		err = shm_checkid(shp,shmid);
531     		if(err)
532     			goto out_unlock_up;
533     		err=-EPERM;
534     		if (current->euid != shp->shm_perm.uid &&
535     		    current->euid != shp->shm_perm.cuid && 
536     		    !capable(CAP_SYS_ADMIN)) {
537     			goto out_unlock_up;
538     		}
539     
540     		shp->shm_perm.uid = setbuf.uid;
541     		shp->shm_perm.gid = setbuf.gid;
542     		shp->shm_flags = (shp->shm_flags & ~S_IRWXUGO)
543     			| (setbuf.mode & S_IRWXUGO);
544     		shp->shm_ctim = CURRENT_TIME;
545     		break;
546     	}
547     
548     	default:
549     		return -EINVAL;
550     	}
551     
552     	err = 0;
553     out_unlock_up:
554     	shm_unlock(shmid);
555     out_up:
556     	up(&shm_ids.sem);
557     	return err;
558     out_unlock:
559     	shm_unlock(shmid);
560     	return err;
561     }
562     
563     /*
564      * Fix shmaddr, allocate descriptor, map shm, add attach descriptor to lists.
565      */
566     asmlinkage long sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr)
567     {
568     	struct shmid_kernel *shp;
569     	unsigned long addr;
570     	struct file * file;
571     	int    err;
572     	unsigned long flags;
573     	unsigned long prot;
574     	unsigned long o_flags;
575     	int acc_mode;
576     	void *user_addr;
577     
578     	if (shmid < 0)
579     		return -EINVAL;
580     
581     	if ((addr = (ulong)shmaddr)) {
582     		if (addr & (SHMLBA-1)) {
583     			if (shmflg & SHM_RND)
584     				addr &= ~(SHMLBA-1);	   /* round down */
585     			else
586     				return -EINVAL;
587     		}
588     		flags = MAP_SHARED | MAP_FIXED;
589     	} else
590     		flags = MAP_SHARED;
591     
592     	if (shmflg & SHM_RDONLY) {
593     		prot = PROT_READ;
594     		o_flags = O_RDONLY;
595     		acc_mode = S_IRUGO;
596     	} else {
597     		prot = PROT_READ | PROT_WRITE;
598     		o_flags = O_RDWR;
599     		acc_mode = S_IRUGO | S_IWUGO;
600     	}
601     
602     	/*
603     	 * We cannot rely on the fs check since SYSV IPC does have an
604     	 * aditional creator id...
605     	 */
606     	shp = shm_lock(shmid);
607     	if(shp == NULL)
608     		return -EINVAL;
609     	err = shm_checkid(shp,shmid);
610     	if (err) {
611     		shm_unlock(shmid);
612     		return err;
613     	}
614     	if (ipcperms(&shp->shm_perm, acc_mode)) {
615     		shm_unlock(shmid);
616     		return -EACCES;
617     	}
618     	file = shp->shm_file;
619     	shp->shm_nattch++;
620     	shm_unlock(shmid);
621     
622     	down_write(&current->mm->mmap_sem);
623     	user_addr = (void *) do_mmap (file, addr, file->f_dentry->d_inode->i_size, prot, flags, 0);
624     	up_write(&current->mm->mmap_sem);
625     
626     	down (&shm_ids.sem);
627     	if(!(shp = shm_lock(shmid)))
628     		BUG();
629     	shp->shm_nattch--;
630     	if(shp->shm_nattch == 0 &&
631     	   shp->shm_flags & SHM_DEST)
632     		shm_destroy (shp);
633     	shm_unlock(shmid);
634     	up (&shm_ids.sem);
635     
636     	*raddr = (unsigned long) user_addr;
637     	err = 0;
638     	if (IS_ERR(user_addr))
639     		err = PTR_ERR(user_addr);
640     	return err;
641     
642     }
643     
644     /*
645      * detach and kill segment if marked destroyed.
646      * The work is done in shm_close.
647      */
648     asmlinkage long sys_shmdt (char *shmaddr)
649     {
650     	struct mm_struct *mm = current->mm;
651     	struct vm_area_struct *shmd, *shmdnext;
652     
653     	down_write(&mm->mmap_sem);
654     	for (shmd = mm->mmap; shmd; shmd = shmdnext) {
655     		shmdnext = shmd->vm_next;
656     		if (shmd->vm_ops == &shm_vm_ops
657     		    && shmd->vm_start - (shmd->vm_pgoff << PAGE_SHIFT) == (ulong) shmaddr)
658     			do_munmap(mm, shmd->vm_start, shmd->vm_end - shmd->vm_start);
659     	}
660     	up_write(&mm->mmap_sem);
661     	return 0;
662     }
663     
664     #ifdef CONFIG_PROC_FS
665     static int sysvipc_shm_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data)
666     {
667     	off_t pos = 0;
668     	off_t begin = 0;
669     	int i, len = 0;
670     
671     	down(&shm_ids.sem);
672     	len += sprintf(buffer, "       key      shmid perms       size  cpid  lpid nattch   uid   gid  cuid  cgid      atime      dtime      ctime\n");
673     
674     	for(i = 0; i <= shm_ids.max_id; i++) {
675     		struct shmid_kernel* shp;
676     
677     		shp = shm_lock(i);
678     		if(shp!=NULL) {
679     #define SMALL_STRING "%10d %10d  %4o %10u %5u %5u  %5d %5u %5u %5u %5u %10lu %10lu %10lu\n"
680     #define BIG_STRING   "%10d %10d  %4o %21u %5u %5u  %5d %5u %5u %5u %5u %10lu %10lu %10lu\n"
681     			char *format;
682     
683     			if (sizeof(size_t) <= sizeof(int))
684     				format = SMALL_STRING;
685     			else
686     				format = BIG_STRING;
687     			len += sprintf(buffer + len, format,
688     				shp->shm_perm.key,
689     				shm_buildid(i, shp->shm_perm.seq),
690     				shp->shm_flags,
691     				shp->shm_segsz,
692     				shp->shm_cprid,
693     				shp->shm_lprid,
694     				shp->shm_nattch,
695     				shp->shm_perm.uid,
696     				shp->shm_perm.gid,
697     				shp->shm_perm.cuid,
698     				shp->shm_perm.cgid,
699     				shp->shm_atim,
700     				shp->shm_dtim,
701     				shp->shm_ctim);
702     			shm_unlock(i);
703     
704     			pos += len;
705     			if(pos < offset) {
706     				len = 0;
707     				begin = pos;
708     			}
709     			if(pos > offset + length)
710     				goto done;
711     		}
712     	}
713     	*eof = 1;
714     done:
715     	up(&shm_ids.sem);
716     	*start = buffer + (offset - begin);
717     	len -= (offset - begin);
718     	if(len > length)
719     		len = length;
720     	if(len < 0)
721     		len = 0;
722     	return len;
723     }
724     #endif
725