1 //
2 // This is part of dvd+rw-tools by Andy Polyakov <appro@fy.chalmers.se>
3 //
4 // Use-it-on-your-own-risk, GPL bless...
5 //
6 // For further details see http://fy.chalmers.se/~appro/linux/DVD+RW/
7 //
8 
9 #if defined(__unix) || defined(__unix__)
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <unistd.h>
13 #include <string.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <fcntl.h>
17 #include <poll.h>
18 #include <sys/time.h>
19 
20 inline long getmsecs()
21 { struct timeval tv;
22     gettimeofday (&tv,NULL);
23   return tv.tv_sec*1000+tv.tv_usec/1000;
24 }
25 
26 #include <errno.h>
27 
28 #ifndef EMEDIUMTYPE
29 #define EMEDIUMTYPE	EINVAL
30 #endif
31 #ifndef	ENOMEDIUM
32 #define	ENOMEDIUM	ENODEV
33 #endif
34 
35 #include <locale.h>
36 #define ENV_LOCALE	""
37 
38 #elif defined(_WIN32)
39 #include <windows.h>
40 #include <stddef.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #define ssize_t		LONG_PTR
44 #define off64_t		__int64
45 
mgr_ldap_entries_class_init(MgrLdapEntriesClass * klass)46 #include "win32err.h"
47 
48 #define poll(a,b,t)	Sleep(t)
49 #define getmsecs()	GetTickCount()
50 
51 #include <locale.h>
52 #define ENV_LOCALE	".OCP"
53 #endif
54 
55 #include "asctable.h"
56 
57 #define CREAM_ON_ERRNO_NAKED(s)				\
58     switch ((s)[12])					\
59     {	case 0x04:	errno=EAGAIN;	break;		\
60 	case 0x20:	errno=ENODEV;	break;		\
61 	case 0x21:	if ((s)[13]==0)	errno=ENOSPC;	\
62 			else		errno=EINVAL;	\
63 			break;				\
64 	case 0x30:	errno=EMEDIUMTYPE;	break;	\
65 	case 0x3A:	errno=ENOMEDIUM;	break;	\
66     }
mgr_ldap_entries_dispose(GObject * object)67 #define CREAM_ON_ERRNO(s)	do { CREAM_ON_ERRNO_NAKED(s) } while(0)
68 
69 #ifndef FATAL_START
70 #define	FATAL_START(er)	(0x80|(er))
71 #endif
72 #define ERRCODE(s)	((((s)[2]&0x0F)<<16)|((s)[12]<<8)|((s)[13]))
73 #define	SK(errcode)	(((errcode)>>16)&0xF)
74 #define	ASC(errcode)	(((errcode)>>8)&0xFF)
75 #define ASCQ(errcode)	((errcode)&0xFF)
76 
77 static void sperror (const char *cmd,int err)
78 { int saved_errno=errno;
79   const char *msg;
80 
81     if (err==-1)
82 	fprintf (stderr,":-( unable to %s: ",cmd);
83     else if ((msg=ASC_lookup(err))!=NULL)
84 	fprintf (stderr,":-[ %s failed with SK=%Xh/%s]: ",
85 			cmd,SK(err),msg);
86     else
87 	fprintf (stderr,":-[ %s failed with SK=%Xh/ASC=%02Xh/ACQ=%02Xh]: ",
88 			cmd,SK(err),ASC(err),ASCQ(err));
89     errno=saved_errno, perror (NULL);
90 }
mgr_ldap_entries_get_type(void)91 
92 static void sperror (const char *cmd,unsigned char *sense)
93 { int saved_errno=errno;
94   int err=ERRCODE(sense);
95 
96     if (err==0)
97 	fprintf (stderr,":-( unable to %s: ",cmd);
98     else
99     {	if ((err==0x20407 || err==0x20408) && sense[15]&0x80)
100 	    fprintf (stderr,":-[ %s failed with SK=%Xh/ASC=%02Xh/ACQ=%02Xh@%.1f%%]: ",
101 			cmd,SK(err),ASC(err),ASCQ(err),
102 			100.0*(sense[16]<<8|sense[17])/65536.0);
103     	else
104 	{   errno=saved_errno;
105 	    sperror (cmd,err);
106 	    return;
107 	}
108     }
109     errno=saved_errno, perror (NULL);
110 }
111 
112 class autofree {
113     private:
114 	unsigned char *ptr;
115     public:
116 	autofree()			{ ptr=NULL; }
117 	~autofree()			{ if (ptr) free(ptr); }
118 	unsigned char *operator=(unsigned char *str)
119 					{ return ptr=str; }
120 	operator unsigned char *()	{ return ptr; }
121 };
122 
123 extern "C" char *plusminus_locale()
124 { static class __plusminus {
125     private:
126 	char str[4];
127     public:
128 	__plusminus()	{   setlocale(LC_CTYPE,ENV_LOCALE);
129 			    int l = wctomb(str,(wchar_t)(unsigned char)'�');
mgr_ldap_entries_new(BrowserConnection * bcnc,const gchar * dn)130 			    if (l>0)	str[l]='\0';
131 			    else	str[0]='�',str[1]='\0';
132 			}
133 	~__plusminus()	{ }
134 	operator char*(){ return str; }
135   } plusminus;
136 
137   return plusminus;
138 }
139 
140 #if defined(__linux)
141 
142 #include <sys/ioctl.h>
143 #include <linux/cdrom.h>
144 #include <mntent.h>
145 #include <sys/wait.h>
146 #include <sys/utsname.h>
147 #include <scsi/sg.h>
148 #if !defined(SG_FLAG_LUN_INHIBIT)
149 # if defined(SG_FLAG_UNUSED_LUN_INHIBIT)
150 #  define SG_FLAG_LUN_INHIBIT SG_FLAG_UNUSED_LUN_INHIBIT
update_children_cb(G_GNUC_UNUSED BrowserConnection * bcnc,gpointer out_result,AsyncExecData * data,GError * error)151 # else
152 #  define SG_FLAG_LUN_INHIBIT 0
153 # endif
154 #endif
155 #ifndef CHECK_CONDITION
156 #define CHECK_CONDITION 0x01
157 #endif
158 
159 typedef enum {	NONE=CGC_DATA_NONE,	// 3
160 		READ=CGC_DATA_READ,	// 2
161 		WRITE=CGC_DATA_WRITE	// 1
162 	     } Direction;
163 #ifdef SG_IO
164 static const int Dir_xlate [4] = {	// should have been defined
165 					// private in USE_SG_IO scope,
166 					// but it appears to be too
167 		0,			// implementation-dependent...
168 		SG_DXFER_TO_DEV,	// 1,CGC_DATA_WRITE
169 		SG_DXFER_FROM_DEV,	// 2,CGC_DATA_READ
170 		SG_DXFER_NONE	};	// 3,CGC_DATA_NONE
171 static const class USE_SG_IO {
172 private:
173     int	yes_or_no;
174 public:
175     USE_SG_IO()	{ struct utsname buf;
176 		    uname (&buf);
177 		    // was CDROM_SEND_PACKET declared dead in 2.5?
178 		    yes_or_no=(strcmp(buf.release,"2.5.43")>=0);
179 		}
180     ~USE_SG_IO(){}
181     operator int()			const	{ return yes_or_no; }
182     int operator[] (Direction dir)	const	{ return Dir_xlate[dir]; }
183 } use_sg_io;
mgr_ldap_entries_update_children(GdaTreeManager * manager,GdaTreeNode * node,G_GNUC_UNUSED const GSList * children_nodes,gboolean * out_error,GError ** error)184 #endif
185 
186 #if 0
187 #include <dlfcn.h>
188 
189 static union dl_rsm_open_device {
190     void *p;
191     int (*f)(const char *,int,...);
192 
193     dl_rsm_open_device(){   void *h;
194 			    if ((h=dlopen("libresmgr.so.1",RTLD_LAZY))==NULL ||
195 				(p=dlsym(h,"rsm_open_device"))==NULL)
196 				f = open;
197 			}
198     ~dl_rsm_open_device(){}
199 } rsm_open_device;
200 
201 extern "C" int dev_open(const char *pathname,int flags)
202 { return rsm_open_device.f(pathname,flags); }
203 extern "C" int dev_open_patched()
204 { return rsm_open_device.f!=open; }
205 #endif
206 
207 class Scsi_Command {
208 private:
209     int fd,autoclose;
210     char *filename;
211     struct cdrom_generic_command cgc;
212     union {
213 	struct request_sense	s;
214 	unsigned char		u[18];
215     } _sense;
216 #ifdef SG_IO
217     struct sg_io_hdr		sg_io;
218 #else
219     struct { int cmd_len,timeout; }	sg_io;
220 #endif
221 public:
222     Scsi_Command()	{ fd=-1, autoclose=1; filename=NULL; }
223     Scsi_Command(int f)	{ fd=f,  autoclose=0; filename=NULL; }
224     Scsi_Command(void*f){ fd=(long)f, autoclose=0; filename=NULL; }
225     ~Scsi_Command()	{ if (fd>=0 && autoclose) close(fd),fd=-1;
226 			  if (filename) free(filename),filename=NULL;
227 			}
228     int associate (const char *file,const struct stat *ref=NULL)
229     { struct stat sb;
230 
231 	/*
232 	 * O_RDWR is expected to provide for none set-root-uid
233 	 * execution under Linux kernel 2.6[.8]. Under 2.4 it
234 	 * falls down to O_RDONLY...
235 	 */
236 	if ((fd=open (file,O_RDWR|O_NONBLOCK)) < 0 &&
237 	    (fd=open (file,O_RDONLY|O_NONBLOCK)) < 0)	return 0;
238 	if (fstat(fd,&sb) < 0)				return 0;
239 	if (!S_ISBLK(sb.st_mode))	{ errno=ENOTBLK;return 0; }
240 
241 	if (ref && (!S_ISBLK(ref->st_mode) || ref->st_rdev!=sb.st_rdev))
242 	{   errno=ENXIO; return 0;   }
243 
244 	filename=strdup(file);
245 
246 	return 1;
247     }
248     unsigned char &operator[] (size_t i)
249     {	if (i==0)
250 	{   memset(&cgc,0,sizeof(cgc)), memset(&_sense,0,sizeof(_sense));
251 	    cgc.quiet = 1;
252 	    cgc.sense = &_sense.s;
253 #ifdef SG_IO
254 	    if (use_sg_io)
255 	    {	memset(&sg_io,0,sizeof(sg_io));
256 		sg_io.interface_id= 'S';
257 		sg_io.mx_sb_len	= sizeof(_sense);
258 		sg_io.cmdp	= cgc.cmd;
259 		sg_io.sbp	= _sense.u;
260 		sg_io.flags	= SG_FLAG_LUN_INHIBIT|SG_FLAG_DIRECT_IO;
261 	    }
262 #endif
263 	}
264 	sg_io.cmd_len = i+1;
265 	return cgc.cmd[i];
266     }
267     unsigned char &operator()(size_t i)	{ return _sense.u[i]; }
268     unsigned char *sense()		{ return _sense.u;    }
269     void timeout(int i)			{ cgc.timeout=sg_io.timeout=i*1000; }
270 #ifdef SG_IO
271     size_t residue()			{ return use_sg_io?sg_io.resid:0; }
272 #else
273     size_t residue()			{ return 0; }
274 #endif
275     int transport(Direction dir=NONE,void *buf=NULL,size_t sz=0)
276     { int ret = 0;
277 
278 #ifdef SG_IO
279 #define KERNEL_BROKEN 0
280 	if (use_sg_io)
281 	{   sg_io.dxferp		= buf;
282 	    sg_io.dxfer_len		= sz;
283 	    sg_io.dxfer_direction	= use_sg_io[dir];
284 	    if (ioctl (fd,SG_IO,&sg_io)) return -1;
285 
286 #if !KERNEL_BROKEN
287 	    if ((sg_io.info&SG_INFO_OK_MASK) != SG_INFO_OK)
288 #else
289 	    if (sg_io.status)
290 #endif
291 	    {	errno=EIO; ret=-1;
292 #if !KERNEL_BROKEN
293 		if (sg_io.masked_status&CHECK_CONDITION)
294 #endif
295 		{   ret = ERRCODE(_sense.u);
296 		    if (ret==0) ret=-1;
297 		    else	CREAM_ON_ERRNO(_sense.u);
298 		}
299 	    }
300 	    return ret;
301 	}
302 	else
303 #undef KERNEL_BROKEN
304 #endif
305 	{   cgc.buffer		= (unsigned char *)buf;
306 	    cgc.buflen		= sz;
307 	    cgc.data_direction	= dir;
308 	    if (ioctl (fd,CDROM_SEND_PACKET,&cgc))
309 	    {	ret = ERRCODE(_sense.u);
310 		if (ret==0) ret=-1;
311 	    }
312 	}
313 	return ret;
314     }
315     int umount(int f=-1)
316     { struct stat    fsb,msb;
317       struct mntent *mb;
318       FILE          *fp;
319       pid_t          pid,rpid;
320       int            ret=0,rval;
321 
322 	if (f==-1) f=fd;
323 	if (fstat (f,&fsb) < 0)				return -1;
324 	if ((fp=setmntent ("/proc/mounts","r"))==NULL)	return -1;
325 
326 	while ((mb=getmntent (fp))!=NULL)
327 	{   if (stat (mb->mnt_fsname,&msb) < 0) continue; // corrupted line?
328 #ifdef I_HAVE_PATCHED_SUBMOUNTD	// see O_EXCL commentary in growisofs.c
329 	    if (!strcmp (mb->mnt_type,"subfs")) continue;
330 #endif
331 	    if (msb.st_rdev == fsb.st_rdev)
332 	    {	ret = -1;
333 		if ((pid = fork()) == (pid_t)-1)	break;
334 		if (pid == 0) execl ("/bin/umount","umount",mb->mnt_dir,(void*)NULL);
335 		while (1)
336 		{   rpid = waitpid (pid,&rval,0);
337 		    if (rpid == (pid_t)-1)
338 		    {	if (errno==EINTR)	continue;
339 			else			break;
340 		    }
341 		    else if (rpid != pid)
342 		    {	errno = ECHILD;
343 			break;
344 		    }
345 		    if (WIFEXITED(rval))
346 		    {	if (WEXITSTATUS(rval) == 0) ret=0;
347 			else			    errno=EBUSY; // most likely
348 			break;
349 		    }
350 		    else
351 		    {	errno = ENOLINK;	// some phony errno
352 			break;
353 		    }
354 		}
355 		break;
356 	    }
357 	}
358 	endmntent (fp);
359 
360 	return ret;
361     }
362     int is_reload_needed (int same_capacity)
363     {	if (same_capacity && ioctl (fd,0x1261,0) == 0) // try BLKFLSBUF
364 	    return 0;
365 	else
366 	    return ioctl (fd,CDROM_MEDIA_CHANGED,CDSL_CURRENT) == 0;
367     }
368 };
369 
370 #elif defined(__OpenBSD__) || defined(__NetBSD__)
371 
372 #include <sys/ioctl.h>
373 #include <sys/scsiio.h>
374 #include <sys/wait.h>
375 #include <sys/param.h>
376 #include <sys/mount.h>
377 
378 typedef off_t off64_t;
379 #define stat64   stat
380 #define fstat64  fstat
381 #define open64   open
382 #define pread64	 pread
383 #define pwrite64 pwrite
384 #define lseek64  lseek
385 
386 typedef enum {	NONE=0,
387 		READ=SCCMD_READ,
388 		WRITE=SCCMD_WRITE
389 	     } Direction;
390 
391 class Scsi_Command {
392 private:
393     int fd,autoclose;
394     char *filename;
395     scsireq_t req;
396 public:
397     Scsi_Command()	{ fd=-1, autoclose=1; filename=NULL; }
398     Scsi_Command(int f)	{ fd=f,  autoclose=0; filename=NULL; }
399     Scsi_Command(void*f){ fd=(long)f, autoclose=0; filename=NULL; }
400     ~Scsi_Command()	{ if (fd>=0 && autoclose) close(fd),fd=-1;
401 			  if (filename) free(filename),filename=NULL;
402 			}
403     int associate (const char *file,const struct stat *ref=NULL)
404     { struct stat sb;
405 
406 	fd=open(file,O_RDWR|O_NONBLOCK);
407 	// this is --^^^^^^-- why we have to run set-root-uid...
408 
409 	if (fd < 0)					return 0;
410 	if (fstat(fd,&sb) < 0)				return 0;
411 	if (!S_ISCHR(sb.st_mode))	{ errno=EINVAL; return 0; }
412 
413 	if (ref && (!S_ISCHR(ref->st_mode) || ref->st_rdev!=sb.st_rdev))
414 	{   errno=ENXIO; return 0;   }
415 
416 	filename=strdup(file);
417 
418 	return 1;
419     }
420     unsigned char &operator[] (size_t i)
421     {	if (i==0)
422 	{   memset(&req,0,sizeof(req));
423 	    req.flags = SCCMD_ESCAPE;
424 	    req.timeout = 30000;
425 	    req.senselen = 18; //sizeof(req.sense);
426 	}
427 	req.cmdlen = i+1;
428 	return req.cmd[i];
429     }
430     unsigned char &operator()(size_t i)	{ return req.sense[i]; }
431     unsigned char *sense()		{ return req.sense;    }
432     void timeout(int i)			{ req.timeout=i*1000; }
433     size_t residue()			{ return req.datalen-req.datalen_used; }
434     int transport(Direction dir=NONE,void *buf=NULL,size_t sz=0)
435     { int ret=0;
436 
437 	req.databuf = (caddr_t)buf;
438 	req.datalen = sz;
439 	req.flags |= dir;
440 	if (ioctl (fd,SCIOCCOMMAND,&req) < 0)	return -1;
441 	if (req.retsts==SCCMD_OK)		return 0;
442 
443 	errno=EIO; ret=-1;
444 	if (req.retsts==SCCMD_SENSE)
445 	{   ret = ERRCODE(req.sense);
446 	    if (ret==0) ret=-1;
447 	    else	CREAM_ON_ERRNO(req.sense);
448 	}
449 	return ret;
450     }
451     // this code is basically redundant... indeed, we normally want to
452     // open device O_RDWR, but we can't do that as long as it's mounted.
453     // in other words, whenever this routine is invoked, device is not
454     // mounted, so that it could as well just return 0;
455     int umount(int f=-1)
456     { struct stat    fsb,msb;
457       struct statfs *mntbuf;
458       int            ret=0,mntsize,i;
459 
460 	if (f==-1) f=fd;
461 
462 	if (fstat (f,&fsb) < 0)				return -1;
463 	if ((mntsize=getmntinfo(&mntbuf,MNT_NOWAIT))==0)return -1;
464 
465 	for (i=0;i<mntsize;i++)
466 	{ char rdev[MNAMELEN+1],*slash,*rslash;
467 
468 	    mntbuf[i].f_mntfromname[MNAMELEN-1]='\0';	// paranoia
469 	    if ((slash=strrchr (mntbuf[i].f_mntfromname,'/'))==NULL) continue;
470 	    strcpy (rdev,mntbuf[i].f_mntfromname); // rdev is 1 byte larger!
471 	    rslash = strrchr  (rdev,'/');
472 	    *(rslash+1) = 'r', strcpy (rslash+2,slash+1);
473 	    if (stat (rdev,&msb) < 0) continue;
474 	    if (msb.st_rdev == fsb.st_rdev)
475 	    {	ret=unmount (mntbuf[i].f_mntonname,0);
476 		break;
477             }
478 	}
479 
480 	return ret;
481     }
482     int is_reload_needed (int not_used)
483     {	return 1;   }
484 };
485 
486 #elif defined(__FreeBSD__) || defined(__DragonFly__)
487 
488 #include <sys/ioctl.h>
489 #include <camlib.h>
490 #include <bus/cam/scsi/scsi_message.h>
491 #include <bus/cam/scsi/scsi_pass.h>
492 #include <sys/wait.h>
493 #include <sys/param.h>
494 #include <sys/mount.h>
495 #include <dirent.h>
496 
497 typedef off_t off64_t;
498 #define stat64   stat
499 #define fstat64  fstat
500 #define open64   open
501 #define pread64  pread
502 #define pwrite64 pwrite
503 #define lseek64  lseek
504 
505 #define ioctl_fd (((struct cam_device *)ioctl_handle)->fd)
506 
507 typedef enum {	NONE=CAM_DIR_NONE,
508 		READ=CAM_DIR_IN,
509 		WRITE=CAM_DIR_OUT
510 	     } Direction;
511 
512 class Scsi_Command {
513 private:
514     int fd,autoclose;
515     char *filename;
516     struct cam_device  *cam;
517     union ccb		ccb;
518 public:
519     Scsi_Command()
520     {	cam=NULL, fd=-1, autoclose=1; filename=NULL;   }
521     Scsi_Command(int f)
522     {	char pass[32];	// periph_name is 16 chars long
523 
524 	cam=NULL, fd=-1, autoclose=1, filename=NULL;
525 
526 	memset (&ccb,0,sizeof(ccb));
527 	ccb.ccb_h.func_code = XPT_GDEVLIST;
528 	if (ioctl (f,CAMGETPASSTHRU,&ccb) < 0) return;
529 
530 	sprintf (pass,"/dev/%.15s%u",ccb.cgdl.periph_name,ccb.cgdl.unit_number);
531 	cam=cam_open_pass (pass,O_RDWR,NULL);
532     }
533     Scsi_Command(void *f)
534     {	cam=(struct cam_device *)f, autoclose=0; fd=-1; filename=NULL;  }
535     ~Scsi_Command()
536     {	if (cam && autoclose)	cam_close_device(cam), cam=NULL;
537 	if (fd>=0)		close(fd);
538 	if (filename)		free(filename), filename=NULL;
539     }
540 
541     int associate (const char *file,const struct stat *ref=NULL)
542     {	struct stat sb;
543 	char pass[32];		// periph_name is 16 chars long
544 
545 	fd=open(file,O_RDONLY|O_NONBLOCK);
546 
547 	// all if (ref) code is actually redundant, it never runs
548 	// as long as RELOAD_NEVER_NEEDED...
549 	if (ref && fd<0 && errno==EPERM)
550 	{   // expectedly we would get here if file is /dev/passN
551 	    if (stat(file,&sb) < 0)		return 0;
552 	    if (!S_ISCHR(ref->st_mode) || ref->st_rdev!=sb.st_rdev)
553 		return (errno=ENXIO,0);
554 	    fd=open(file,O_RDWR);
555 	}
556 
557 	if (fd < 0)				return 0;
558 	if (fstat(fd,&sb) < 0)			return 0;
559 	if (!S_ISCHR(sb.st_mode))		return (errno=EINVAL,0);
560 
561 	if (ref && (!S_ISCHR(ref->st_mode) || ref->st_rdev!=sb.st_rdev))
562 	    return (errno=ENXIO,0);
563 
564 	memset (&ccb,0,sizeof(ccb));
565 	ccb.ccb_h.func_code = XPT_GDEVLIST;
566 	if (ioctl(fd,CAMGETPASSTHRU,&ccb)<0)	return (close(fd),fd=-1,0);
567 
568 	sprintf (pass,"/dev/%.15s%u",ccb.cgdl.periph_name,ccb.cgdl.unit_number);
569 	cam=cam_open_pass (pass,O_RDWR,NULL);
570 	if (cam==NULL)				return (close(fd),fd=-1,0);
571 
572 	filename=strdup(file);
573 
574 	return 1;
575     }
576     unsigned char &operator[] (size_t i)
577     {	if (i==0)
578 	{   memset(&ccb,0,sizeof(ccb));
579 	    ccb.ccb_h.path_id    = cam->path_id;
580 	    ccb.ccb_h.target_id  = cam->target_id;
581 	    ccb.ccb_h.target_lun = cam->target_lun;
582 	    cam_fill_csio (&(ccb.csio),
583 			1,				// retries
584 			NULL,				// cbfncp
585 			CAM_DEV_QFRZDIS,		// flags
586 			MSG_SIMPLE_Q_TAG,		// tag_action
587 			NULL,				// data_ptr
588 			0,				// dxfer_len
589 			sizeof(ccb.csio.sense_data),	// sense_len
590 			0,				// cdb_len
591 			30*1000);			// timeout
592 	}
593 	ccb.csio.cdb_len = i+1;
594 	return ccb.csio.cdb_io.cdb_bytes[i];
595     }
596     unsigned char &operator()(size_t i)
597 			{ return ((unsigned char *)&ccb.csio.sense_data)[i]; }
598     unsigned char *sense()
599 			{ return (unsigned char*)&ccb.csio.sense_data;    }
600     void timeout(int i)	{ ccb.ccb_h.timeout=i*1000; }
601     size_t residue()	{ return ccb.csio.resid; }
602     int transport(Direction dir=NONE,void *buf=NULL,size_t sz=0)
603     {	int ret=0;
604 
605 	ccb.csio.ccb_h.flags |= dir;
606 	ccb.csio.data_ptr  = (u_int8_t *)buf;
607 	ccb.csio.dxfer_len = sz;
608 
609 	if ((ret = cam_send_ccb(cam, &ccb)) < 0)
610 	    return -1;
611 
612 	if ((ccb.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)
613 	    return 0;
614 
615 	unsigned char  *sense=(unsigned char *)&ccb.csio.sense_data;
616 
617 	errno = EIO;
618 	// FreeBSD 5-CURRENT since 2003-08-24, including 5.2 fails to
619 	// pull sense data automatically, at least for ATAPI transport,
620 	// so I reach for it myself...
621 	if ((ccb.csio.scsi_status==SCSI_STATUS_CHECK_COND) &&
622 	    !(ccb.ccb_h.status&CAM_AUTOSNS_VALID))
623 	{   u_int8_t  _sense[18];
624 	    u_int32_t resid=ccb.csio.resid;
625 
626 	    memset(_sense,0,sizeof(_sense));
627 
628 	    operator[](0)      = 0x03;	// REQUEST SENSE
629 	    ccb.csio.cdb_io.cdb_bytes[4] = sizeof(_sense);
630 	    ccb.csio.cdb_len   = 6;
631 	    ccb.csio.ccb_h.flags |= CAM_DIR_IN|CAM_DIS_AUTOSENSE;
632 	    ccb.csio.data_ptr  = _sense;
633 	    ccb.csio.dxfer_len = sizeof(_sense);
634 	    ccb.csio.sense_len = 0;
635 	    ret = cam_send_ccb(cam, &ccb);
636 
637 	    ccb.csio.resid = resid;
638 	    if (ret<0)	return -1;
639 	    if ((ccb.ccb_h.status&CAM_STATUS_MASK) != CAM_REQ_CMP)
640 		return errno=EIO,-1;
641 
642 	    memcpy(sense,_sense,sizeof(_sense));
643 	}
644 
645 	ret = ERRCODE(sense);
646 	if (ret == 0)	ret = -1;
647 	else		CREAM_ON_ERRNO(sense);
648 
649 	return ret;
650     }
651     int umount(int f=-1)
652     { struct stat    fsb,msb;
653       struct statfs *mntbuf;
654       int            ret=0,mntsize,i;
655 
656 	if (f==-1) f=fd;
657 
658 	if (fstat (f,&fsb) < 0)				return -1;
659 	if ((mntsize=getmntinfo(&mntbuf,MNT_NOWAIT))==0)return -1;
660 
661 	for (i=0;i<mntsize;i++)
662 	{   if (stat (mntbuf[i].f_mntfromname,&msb) < 0) continue;
663 	    if (msb.st_rdev == fsb.st_rdev)
664 	    {	ret=unmount (mntbuf[i].f_mntonname,0);
665 		break;
666 	    }
667 	}
668 
669 	return ret;
670     }
671 #define RELOAD_NEVER_NEEDED	// according to Matthew Dillon
672     int is_reload_needed (int not_used)
673     {  return 0;   }
674 };
675 
676 #elif defined(__sun) || defined(sun)
677 //
678 // Licensing terms for commercial deployment under Solaris are to be
679 // settled with Inserve Technology, �v�gen 6, 412 50 G�TEBORG, Sweden,
680 // tel. +46-(0)31-86 87 88, see http://www.inserve.se/ for further
681 // details about Inserve Technology.
682 //
683 #include <volmgt.h>
684 extern "C" int _dev_unmount(char *); // VolMgt ON Consolidation Private API
685 #include <sys/param.h>
686 #include <sys/scsi/impl/uscsi.h>
687 #include <sys/mount.h>
688 #include <sys/mnttab.h>
689 #include <sys/wait.h>
690 #include <sys/cdio.h>
691 #include <sys/utsname.h>
692 #include <sys/dklabel.h>
693 #include <sys/dkio.h>
694 #include <dlfcn.h>
695 #include <pwd.h>
696 #include <alloca.h>
697 
698 typedef enum {	NONE=0,
699 		READ=USCSI_READ,
700 		WRITE=USCSI_WRITE
701 	     } Direction;
702 
703 class Scsi_Command {
704 private:
705     int fd,autoclose;
706     char *filename;
707     struct uscsi_cmd ucmd;
708     unsigned char cdb[16], _sense[18];
709 public:
710     Scsi_Command()	{ fd=-1, autoclose=1; filename=NULL; }
711     Scsi_Command(int f)	{ fd=f,  autoclose=0; filename=NULL; }
712     Scsi_Command(void*f){ fd=(long)f, autoclose=0; filename=NULL; }
713     ~Scsi_Command()	{ if (fd>=0 && autoclose) close(fd),fd=-1;
714 			  if (filename) free(filename),filename=NULL;
715 			}
716     int associate (const char *file,const struct stat *ref=NULL)
717     { class autofree {
718       private:
719 	char *ptr;
720       public:
721 	autofree()			{ ptr=NULL; }
722 	~autofree()			{ if (ptr) free(ptr); }
723 	char *operator=(char *str)	{ return ptr=str; }
724 	operator char *()		{ return ptr; }
725       } volname,device;
726       struct stat sb;
727       int v;
728       uid_t uid;
729 
730 	if ((uid=getuid())!=0)
731 	{   void *secdb = dlopen("libsecdb.so.1",RTLD_LAZY);
732 	    union { void *p; int (*f)(const char *,const char *); } chkauthattr;
733 
734 	    if (secdb && (chkauthattr.p=dlsym(secdb,"chkauthattr")))
735 	    {	struct passwd *pwd = getpwuid(uid);
736 		if (pwd==NULL || !chkauthattr.f("solaris.device.cdrw",pwd->pw_name))
737 		    return (errno=EACCES),0;
738 	    }
739 	    if (secdb) dlclose(secdb);
740 	}
741 
742 	if ((v=volmgt_running()))
743 	{   if ((volname=volmgt_symname ((char *)file)))
744 	    {	if ((device=media_findname (volname)) == NULL)
745 		    return 0;
746 	    }
747 	    else if ((device=media_findname ((char *)file))==NULL)
748 		return 0;
749 	}
750 	else device=strdup(file);
751 
752 	fd=open (device,O_RDONLY|O_NONBLOCK);
753 	if (fd<0)					return 0;
754 	if (fstat(fd,&sb) < 0)				return 0;
755 	if (!S_ISCHR(sb.st_mode))	{ errno=ENOTTY;	return 0; }
756 
757 	if (ref && (!S_ISCHR(ref->st_mode) || ref->st_rdev!=sb.st_rdev))
758 	{   errno=ENXIO; return 0;   }
759 
760 	filename=strdup(device);
761 
762 	return 1;
763     }
764     unsigned char &operator[] (size_t i)
765     {	if (i==0)
766 	{   memset (&ucmd,0,sizeof(ucmd));
767 	    memset (cdb,0,sizeof(cdb));
768 	    memset (_sense,0,sizeof(_sense));
769 	    ucmd.uscsi_cdb    = (caddr_t)cdb;
770 	    ucmd.uscsi_rqbuf  = (caddr_t)_sense;
771 	    ucmd.uscsi_rqlen  = sizeof(_sense);
772 	    ucmd.uscsi_flags  = USCSI_SILENT  | USCSI_DIAGNOSE |
773 				USCSI_ISOLATE | USCSI_RQENABLE;
774 	    ucmd.uscsi_timeout= 60;
775 	}
776 	ucmd.uscsi_cdblen = i+1;
777 	return cdb[i];
778     }
779     unsigned char &operator()(size_t i)	{ return _sense[i]; }
780     unsigned char *sense()		{ return _sense;    }
781     void timeout(int i)			{ ucmd.uscsi_timeout=i; }
782     size_t residue()			{ return ucmd.uscsi_resid; }
783     int transport(Direction dir=NONE,void *buf=NULL,size_t sz=0)
784     { int ret=0;
785 
786 	ucmd.uscsi_bufaddr = (caddr_t)buf;
787 	ucmd.uscsi_buflen  = sz;
788 	ucmd.uscsi_flags  |= dir;
789 	if (ioctl(fd,USCSICMD,&ucmd))
790 	{   if (errno==EIO && _sense[0]==0)	// USB seems to be broken...
791 	    {	size_t residue=ucmd.uscsi_resid;
792 		memset(cdb,0,sizeof(cdb));
793 		cdb[0]=0x03;			// REQUEST SENSE
794 		cdb[4]=sizeof(_sense);
795 		ucmd.uscsi_cdblen  = 6;
796 		ucmd.uscsi_bufaddr = (caddr_t)_sense;
797 		ucmd.uscsi_buflen  = sizeof(_sense);
798 		ucmd.uscsi_flags   = USCSI_SILENT  | USCSI_DIAGNOSE |
799 				     USCSI_ISOLATE | USCSI_READ;
800 		ucmd.uscsi_timeout = 1;
801 		ret = ioctl(fd,USCSICMD,&ucmd);
802 		ucmd.uscsi_resid = residue;
803 		if (ret) return -1;
804 	    }
805 	    ret = ERRCODE(_sense);
806 	    if (ret==0)	ret=-1;
807 	    //else	CREAM_ON_ERRNO(_sense);
808 	}
809 	return ret;
810     }
811     // mimics umount(2), therefore inconsistent return values
812     int umount(int f=-1)
813     { struct stat   fsb,msb;
814       struct mnttab mb;
815       FILE         *fp;
816       pid_t         pid,rpid;
817       int           ret=0,i,rval;
818 
819 	if (f==-1) f=fd;
820 
821 	if (fstat (f,&fsb) < 0)			return -1;
822 	if ((fp=fopen (MNTTAB,"r")) == NULL)	return -1;
823 
824 	while ((i=getmntent (fp,&mb)) != -1)
825 	{   if (i != 0)				continue; // ignore corrupted lines
826 	    if (stat (mb.mnt_special,&msb) < 0)	continue; // also corrupted?
827 	    if (msb.st_rdev == fsb.st_rdev)
828 	    {	if (_dev_unmount (mb.mnt_special))	break;
829 		{  struct utsname uts;
830 		    if (uname (&uts)>=0 && (strcmp(uts.release,"5.8")>=0 || strlen(uts.release)>3))
831 		    {	// M-m-m-m-m! Solaris 8 or later...
832 			ret = ::umount (mb.mnt_special);
833 			break;
834 		    }
835 		}
836 		ret = -1;
837 		if ((pid = fork()) == (pid_t)-1)	break;
838 		if (pid == 0) execl ("/usr/sbin/umount","umount",mb.mnt_mountp,(void*)NULL);
839 		while (1)
840 		{   rpid = waitpid (pid,&rval,0);
841 		    if (rpid == (pid_t)-1)
842 		    {	if (errno==EINTR)	continue;
843 			else			break;
844 		    }
845 		    else if (rpid != pid)
846 		    {	errno = ECHILD;
847 			break;
848 		    }
849 		    if (WIFEXITED(rval))
850 		    {	if (WEXITSTATUS(rval) == 0)	ret=0;
851 			else				errno=EBUSY; // most likely
852 			break;
853 		    }
854 		    else if (WIFSTOPPED(rval) || WIFCONTINUED(rval))
855 			continue;
856 		    else
857 		    {	errno = ENOLINK;	// some phony errno
858 			break;
859 		    }
860 		}
861 		break;
862 	    }
863 	}
864 	fclose (fp);
865 
866 	return ret;
867     }
868     int is_reload_needed (int not_used)
869     { struct dk_minfo  mi;
870       struct dk_allmap pt;
871 
872 	if (ioctl (fd,DKIOCGMEDIAINFO,&mi) < 0)	return 1;
873 
874 	memset (&pt,0,sizeof(pt));
875 	pt.dka_map[2].dkl_nblk = mi.dki_capacity*(mi.dki_lbsize/DEV_BSIZE);
876 	pt.dka_map[0] = pt.dka_map[2];
877 	if (ioctl (fd,DKIOCSAPART,&pt) < 0)	return 1;
878 
879 	return 0;
880     }
881 };
882 
883 #elif defined(__hpux)
884 //
885 // Copyright (C) 2003  Hewlett-Packard Development Company, L.P.
886 //
887 // This program is free software; you can redistribute it and/or modify
888 // it under the terms of the GNU General Public License as published by
889 // the Free Software Foundation; either version 2 of the License, or
890 // (at your option) any later version.
891 //
892 // This program is distributed in the hope that it will be useful,
893 // but WITHOUT ANY WARRANTY; without even the implied warranty of
894 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
895 // GNU General Public License for more details.
896 //
897 // You should have received a copy of the GNU General Public License
898 // along with this program; if not, write to the Free Software
899 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
900 //
901 // =====================================================================
902 //
903 // The code targets 11i/11.11 and later, but it might just work on
904 // 11.0 as well. For further information contact following HP office
905 //
906 // Hewlett-Packard Company
907 // 3404 E Harmony Road
908 // Fort Collins, CO 80528 USA
909 //
910 #include <strings.h>
911 #include <sys/scsi.h>
912 #include <mntent.h>
913 #ifndef minor
914 #include <sys/mknod.h>
915 #endif
916 
917 typedef enum {  NONE=0,
918                 READ=SCTL_READ,
919                 WRITE=0
920              } Direction;
921 
922 class Scsi_Command {
923 private:
924     int fd;
925     int autoclose;
926     char *filename;
927     struct sctl_io cmd;
928 public:
929     Scsi_Command()	{ fd=-1, autoclose=1; filename=NULL; }
930     Scsi_Command(int f)	{ fd=f,  autoclose=0; filename=NULL; }
931     Scsi_Command(void*f){ fd=(long)f, autoclose=0; filename=NULL; }
932     ~Scsi_Command()	{ if (fd>=0 && autoclose) close(fd),fd=-1;
933                           if (filename) free(filename),filename=NULL;
934 			}
935     int associate (const char *file,const struct stat *ref=NULL)
936     { struct stat sb;
937 
938 	fd=open (file,O_RDONLY|O_NONBLOCK);
939 
940 	if (fd < 0)					return 0;
941 	if (fstat(fd,&sb) < 0)				return 0;
942 	if (!S_ISCHR(sb.st_mode))	{ errno=EINVAL; return 0; }
943 
944 	// shall we check for DIOC_DESCRIBE here?
945 
946 	if (ref && (!S_ISCHR(ref->st_mode) || ref->st_rdev!=sb.st_rdev))
947 	{   errno=ENXIO; return 0;   }
948 
949 	filename=strdup(file);
950 
951 	return 1;
952     }
953     unsigned char &operator[] (size_t i)
954     {	if (i==0)
955 	{   bzero (&cmd,sizeof(struct sctl_io));
956 	    cmd.max_msecs=30*1000;
957 	}
958 	cmd.cdb_length = i+1;
959 	return cmd.cdb[i];
960     }
961     unsigned char &sense(size_t i)  { return cmd.sense[i];  }
962     unsigned char *sense()	    { return cmd.sense;	    }
963     void timeout(int i)		    { cmd.max_msecs=i*1000; }
964     size_t residue()		    { return cmd.data_length-cmd.data_xfer; }
965     int transport(Direction dir=NONE,void *buf=NULL,size_t sz=0)
966     { const char *err = NULL;
967 
968 	cmd.data = buf;
969 	cmd.data_length = sz;
970 	cmd.flags = dir;
971 
972 	if (ioctl (fd,SIOC_IO,&cmd) != 0)	return -1;
973 
974 	if (cmd.cdb_status == S_GOOD)		return 0;
975 
976 	errno = EIO;
977 	switch (cmd.cdb_status)
978 	{   case SCTL_INVALID_REQUEST:	err = "SCTL_INVALID_REQUEST";	break;
979 	    case SCTL_SELECT_TIMEOUT:	err = "SCTL_SELECT_TIMEOUT";	break;
980 	    case SCTL_INCOMPLETE:	err = "SCTL_INCOMPLETE";	break;
981 	    case SCTL_POWERFAIL:	err = "SCTL_POWERFAIL";		break;
982 	    default:			err = NULL;			break;
983 	}
984 	if (err != NULL)
985 	{   fprintf (stderr,":-( FAIL: command failed with %s.\n",err);
986 	    return -1;
987 	}
988 
989 	switch (cmd.cdb_status & 0xff)
990 	{   case S_CHECK_CONDITION:
991 		if (cmd.sense_status==S_GOOD && cmd.sense_xfer!=0)
992 		{   CREAM_ON_ERRNO_NAKED(cmd.sense)	// CREAM_ON_ERRNO
993 							// provokes PA-RISC
994 							// compiler bug...
995 		    return ERRCODE(cmd.sense);
996 		}
997 		else
998 		    fprintf (stderr,":-( FAIL: S_CHECK_CONDITION status, "
999 				    "but no sense data?\n");
1000 		break;
1001 	    case S_BUSY:
1002 		fprintf (stderr,":-( FAIL: S_BUSY condition?\n");
1003 		errno = EAGAIN;
1004 		break;
1005 	    default:
1006 		fprintf (stderr,":-( FAIL: unknown cdb_status=0x%x\n",
1007 				cmd.cdb_status);
1008 		break;
1009 	}
1010 
1011 	return -1;
1012     }
1013     // for now we only detect if media is mounted...
1014     int umount(int f=-1)
1015     { struct stat    fsb,msb;
1016       struct mntent *mb;
1017       FILE          *fp;
1018       int            ret=0;
1019       char           bdev[32];
1020       dev_t          m;
1021 
1022 	if (f==-1) f=fd;
1023 
1024 	if (fstat (f,&fsb) < 0)				return -1;
1025 	if ((fp=setmntent (MNT_MNTTAB,"r")) == NULL)	return -1;
1026 
1027 	m=minor(fsb.st_rdev);
1028 	sprintf(bdev,"/dev/dsk/c%ut%ud%x",(m>>16)&0xFF,(m>>12)&0xF,(m>>8)&0xF);
1029 	if (stat (bdev,&fsb) < 0)			return -1;
1030 
1031 	while ((mb=getmntent (fp))!=NULL)
1032 	{   if (stat (mb->mnt_fsname,&msb) < 0) continue; // corrupted line?
1033 	    if (msb.st_rdev == fsb.st_rdev)
1034 	    {	errno=EBUSY; ret=-1; break;	}
1035 	}
1036 	endmntent (fp);
1037 
1038 	return ret;
1039     }
1040     int is_reload_needed (int not_used)
1041     {	return 1;   }
1042 };
1043 
1044 #elif defined(__sgi)
1045 //
1046 // Not necessarily relevant IRIX notes.
1047 //
1048 // Information about UDF support seems to be contradictory. Some manuals
1049 // maintain that UDF writing is supported for DVD-RAM and hard disk, but
1050 // the only mention of UDF in IRIX release notes is "6.5.18 introduces
1051 // read-only support for the UDF filesystems format." If UDF writing is
1052 // supported, then it was implemented presumably in 6.5.21. DVD-RAM
1053 // writing at block device level was most likely introduced by then too.
1054 //
1055 // IRIX doesn't provide access to files larger than 2GB on ISO9660.
1056 // That's because ISO9660 is implemented as NFSv2 user-land server,
1057 // and 2GB limit is implied by NFSv2 protocol.
1058 //
1059 
1060 #ifdef PRIVATE
1061 #undef PRIVATE	// <sys/dsreq.h> conflicts with <sys/mman.h>?
1062 #endif
1063 #include <sys/dsreq.h>
1064 #ifdef PRIVATE
1065 #undef PRIVATE	// <sys/dsreq.h> conflicts with <sys/mman.h>?
1066 #endif
1067 #include <mntent.h>
1068 #include <sys/wait.h>
1069 #include <poll.h>
1070 #include <sys/attributes.h>
1071 #include <sys/param.h>
1072 #include <mediad.h>
1073 
1074 typedef enum {	NONE=0,
1075 		READ=DSRQ_READ,
1076 		WRITE=DSRQ_WRITE
1077 	     } Direction;
1078 
1079 class Scsi_Command {
1080 private:
1081     int fd,autoclose;
1082     char *filename;
1083     dsreq_t req;
1084     unsigned char cdb[16], _sense[18];
1085 public:
1086     Scsi_Command()	{ fd=-1, autoclose=1; filename=NULL; }
1087     Scsi_Command(int f)	{ fd=f,  autoclose=0; filename=NULL; }
1088     Scsi_Command(void*f){ fd=(long)f, autoclose=0; filename=NULL; }
1089     ~Scsi_Command()	{ if (fd>=0 && autoclose) close(fd),fd=-1;
1090 			  if (filename) free(filename),filename=NULL;
1091 			}
1092     int associate (const char *file,const struct stat *ref=NULL)
1093     { struct stat sb;
1094       char hw_path[MAXPATHLEN];
1095       int  hw_len=sizeof(hw_path)-1;
1096 
1097 	if (attr_get(file,"_devname",hw_path,&hw_len,0))	return 0;
1098 	if (hw_len>=sizeof(hw_path)) hw_len=sizeof(hw_path)-1;	// paranoia
1099 	hw_path[hw_len]='\0';
1100 
1101 	if (ref)
1102 	{   // hw_path is maintained by kernel through hwgfs and
1103 	    // I assume it's not subject to race conditions...
1104 	    if (stat(hw_path,&sb))	return 0;
1105             if (!S_ISCHR(ref->st_mode) || ref->st_rdev!=sb.st_rdev)
1106             {	errno=ENXIO; return 0;   }
1107 	}
1108 
1109 	if (strcmp(hw_path+strlen(hw_path)-5,"/scsi"))
1110 	{   char *s=strstr(hw_path,"/disk/");
1111 	    if (s==NULL)	{   errno=EINVAL; return 0;   }
1112 	    strcpy (s,"/scsi");
1113 	}
1114 	fd=open (hw_path,O_RDONLY|O_NONBLOCK);
1115 	if (fd<0)					return 0;
1116 	if (fstat(fd,&sb) < 0)				return 0;
1117 	if (!S_ISCHR(sb.st_mode))	{ errno=ENOTTY;	return 0; }
1118 
1119 	filename=strdup(file);
1120 
1121 	return 1;
1122     }
1123     unsigned char &operator[] (size_t i)
1124     {	if (i==0)
1125 	{   memset (&req,0,sizeof(req));
1126 	    memset (cdb,0,sizeof(cdb));
1127 	    memset (_sense,0,sizeof(_sense));
1128 	    req.ds_cmdbuf   = (caddr_t)cdb;
1129 	    req.ds_sensebuf = (caddr_t)_sense;
1130 	    req.ds_senselen = sizeof(_sense);
1131 	    req.ds_flags    = DSRQ_SENSE;
1132 	    req.ds_time     = 60*1000;
1133 	}
1134 	req.ds_cmdlen = i+1;
1135 	return cdb[i];
1136     }
1137     unsigned char &operator()(size_t i)	{ return _sense[i]; }
1138     unsigned char *sense()		{ return _sense;    }
1139     void timeout(int i)			{ req.ds_time=i*1000; }
1140     size_t residue()			{ return req.ds_datalen-req.ds_datasent; }
1141     int transport(Direction dir=NONE,void *buf=NULL,size_t sz=0)
1142     { int ret=0,retries=3;
1143 
1144 	req.ds_databuf = (caddr_t)buf;
1145 	req.ds_datalen = sz;
1146 	req.ds_flags  |= dir;
1147 
1148 	// I personally don't understand why do we need to loop, but
1149 	// /usr/share/src/irix/examples/scsi/lib/src/dslib.c is looping...
1150 	while (retries--)
1151 	{   if (ioctl(fd,DS_ENTER,&req) < 0)	return -1;
1152 	    if (req.ds_status==STA_GOOD)	return 0;
1153 
1154 	    if (req.ds_ret==DSRT_NOSEL)		continue;
1155 	    if (req.ds_status==STA_BUSY || req.ds_status==STA_RESERV)
1156 	    {	poll(NULL,0,500); continue;   }
1157 
1158 	    break;
1159 	}
1160 
1161 	errno=EIO; ret=-1;
1162 	if (req.ds_status==STA_CHECK && req.ds_sensesent>=14)
1163 	{   ret = ERRCODE(_sense);
1164 	    if (ret==0)	ret=-1;
1165 	    else	CREAM_ON_ERRNO(_sense);
1166 	}
1167 	return ret;
1168     }
1169     // mimics umount(2), therefore inconsistent return values
1170     int umount(int f=-1)
1171     { struct stat    fsb,msb;
1172       struct mntent *mb;
1173       FILE          *fp;
1174       pid_t          pid,rpid;
1175       int            ret=0,rval;
1176       char           hw_path[MAXPATHLEN];
1177       int            hw_len=sizeof(hw_path)-1;
1178 
1179 	if (f==-1) f=fd;
1180 
1181 	if (fstat (f,&fsb) < 0)	return -1;
1182 
1183 	if (!getenv("MEDIAD_GOT_EXCLUSIVEUSE"))
1184 	{   if (attr_getf (f,"_devname",hw_path,&hw_len,0))	return -1;
1185 	    if (hw_len>=sizeof(hw_path)) hw_len=sizeof(hw_path)-1;// paranoia
1186 	    hw_path[hw_len]='\0';
1187 
1188 	    // mediad even unmounts removable volumes. However! The
1189 	    // locks are "granted" even for unmanaged devices, so
1190 	    // it's not possible to tell if device is ignored through
1191 	    // /etc/config/mediad.config or actually managed. Therefore
1192 	    // I have to pass through own unmount code in either case...
1193 
1194 	    mediad_get_exclusiveuse(hw_path,"dvd+rw-tools");
1195 	    switch (mediad_last_error())
1196 	    {	case RMED_NOERROR:	break;
1197 		case RMED_EACCESS:
1198 		case RMED_ECANTUMOUNT:	errno=EBUSY; return -1;
1199 		case RMED_ENOMEDIAD:	break;
1200 		case -1:	if(errno==ECONNREFUSED)	break;	// no mediad...
1201 				else			return -1;
1202 		default:	errno=ENOTTY; return -1;
1203 	    }
1204 	}
1205 
1206 	if ((fp=setmntent (MOUNTED,"r"))==NULL)	return -1;
1207 
1208 	while ((mb=getmntent (fp))!=NULL)
1209 	{   if (stat (mb->mnt_fsname,&msb) < 0)	continue; // corrupted line?
1210 	    // Following effectively catches only /dev/rdsk/dksXdYvol,
1211 	    // which is sufficient for iso9660 volumes, but not for e.g.
1212 	    // EFS formatted media. I mean code might have to be more
1213 	    // versatile... Wait for feedback...
1214 	    if (msb.st_rdev == fsb.st_rdev)
1215 	    {	ret = -1;
1216 		if ((pid = fork()) == (pid_t)-1)	break;
1217 		if (pid == 0) execl ("/sbin/umount","umount",mb->mnt_dir,(void*)NULL);
1218 		while (1)
1219 		{   rpid = waitpid (pid,&rval,0);
1220 		    if (rpid == (pid_t)-1)
1221 		    {	if (errno==EINTR)	continue;
1222 			else			break;
1223 		    }
1224 		    else if (rpid != pid)
1225 		    {	errno = ECHILD;
1226 			break;
1227 		    }
1228 		    if (WIFEXITED(rval))
1229 		    {	if (WEXITSTATUS(rval) == 0)	ret=0;
1230 			else				errno=EBUSY; // most likely
1231 			break;
1232 		    }
1233 		    else if (WIFSTOPPED(rval) || WIFCONTINUED(rval))
1234 			continue;
1235 		    else
1236 		    {	errno = ENOLINK;	// some phony errno
1237 			break;
1238 		    }
1239 		}
1240 		break;
1241 	    }
1242 	}
1243 	endmntent (fp);
1244 
1245 	return ret;
1246     }
1247 #if 0	// for now just an idea to test...
1248 #define RELOAD_NEVER_NEEDED
1249     int is_reload_needed (int not_used)
1250     {  return 0;   }
1251 #else
1252     int is_reload_needed (int not_used)
1253     {	return 1;   }
1254 #endif
1255 };
1256 
1257 #elif defined(_WIN32)
1258 
1259 #if defined(__MINGW32__)
1260 #include <ddk/ntddscsi.h>
1261 #define FSCTL_LOCK_VOLUME	CTL_CODE(FILE_DEVICE_FILE_SYSTEM,6,METHOD_BUFFERED,FILE_ANY_ACCESS)
1262 #define FSCTL_UNLOCK_VOLUME	CTL_CODE(FILE_DEVICE_FILE_SYSTEM,7,METHOD_BUFFERED,FILE_ANY_ACCESS)
1263 #define FSCTL_DISMOUNT_VOLUME	CTL_CODE(FILE_DEVICE_FILE_SYSTEM,8,METHOD_BUFFERED,FILE_ANY_ACCESS)
1264 #else
1265 #include <winioctl.h>
1266 #include <ntddscsi.h>
1267 #endif
1268 
1269 typedef enum {	NONE=SCSI_IOCTL_DATA_UNSPECIFIED,
1270 		READ=SCSI_IOCTL_DATA_IN,
1271 		WRITE=SCSI_IOCTL_DATA_OUT
1272 	     } Direction;
1273 
1274 typedef struct {
1275     SCSI_PASS_THROUGH_DIRECT	spt;
1276     unsigned char		sense[18];
1277 } SPKG;
1278 
1279 class Scsi_Command {
1280 private:
1281     HANDLE fd;
1282     int    autoclose;
1283     char  *filename;
1284     SPKG   p;
1285 public:
1286     Scsi_Command()	{ fd=INVALID_HANDLE_VALUE; autoclose=1; filename=NULL; }
1287     Scsi_Command(void*f){ fd=f, autoclose=0; filename=NULL; }
1288     ~Scsi_Command()	{ DWORD junk;
1289 			    if (fd!=INVALID_HANDLE_VALUE && autoclose)
1290 			    {   if (autoclose>1)
1291 				    DeviceIoControl(fd,FSCTL_UNLOCK_VOLUME,
1292 						NULL,0,NULL,0,&junk,NULL);
1293 				CloseHandle (fd),fd=INVALID_HANDLE_VALUE;
1294 			    }
1295 			    if (filename) free(filename),filename=NULL;
1296 			}
1297     int associate (const char *file,const struct stat *ref=NULL)
1298     { char dev[32];
1299 	sprintf(dev,"%.*s\\",sizeof(dev)-2,file);
1300 	if (GetDriveType(dev)!=DRIVE_CDROM)
1301 	    return errno=EINVAL,0;
1302 	sprintf(dev,"\\\\.\\%.*s",sizeof(dev)-5,file);
1303 	fd=CreateFile (dev,GENERIC_WRITE|GENERIC_READ,
1304 			   FILE_SHARE_READ|FILE_SHARE_WRITE,
1305 			   NULL,OPEN_EXISTING,0,NULL);
1306 	if (fd!=INVALID_HANDLE_VALUE)
1307 	    filename=strdup(dev);
1308 	return fd!=INVALID_HANDLE_VALUE;
1309     }
1310     unsigned char &operator[] (size_t i)
1311     {	if (i==0)
1312 	{   memset(&p,0,sizeof(p));
1313 	    p.spt.Length = sizeof(p.spt);
1314 	    p.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
1315 	    p.spt.TimeOutValue = 30;
1316 	    p.spt.SenseInfoLength = sizeof(p.sense);
1317 	    p.spt.SenseInfoOffset = offsetof(SPKG,sense);
1318 	}
1319 	p.spt.CdbLength = i+1;
1320 	return p.spt.Cdb[i];
1321     }
1322     unsigned char &operator()(size_t i)	{ return p.sense[i]; }
1323     unsigned char *sense()		{ return p.sense;    }
1324     void timeout(int i)			{ p.spt.TimeOutValue=i; }
1325     size_t residue()			{ return 0; } // bogus
1326     int transport(Direction dir=NONE,void *buf=NULL,size_t sz=0)
1327     { DWORD bytes;
1328       int   ret=0;
1329 
1330 	p.spt.DataBuffer = buf;
1331 	p.spt.DataTransferLength = sz;
1332 	p.spt.DataIn = dir;
1333 
1334 	if (DeviceIoControl (fd,IOCTL_SCSI_PASS_THROUGH_DIRECT,
1335 				&p,sizeof(p.spt),
1336 				&p,sizeof(p),
1337 				&bytes,FALSE) == 0) return -1;
1338 
1339 	if (p.sense[0]&0x70)
1340 	{   SetLastError (ERROR_GEN_FAILURE);
1341 	    ret = ERRCODE(p.sense);
1342 	    if (ret==0) ret=-1;
1343 	    else	CREAM_ON_ERRNO(p.sense);
1344 	}
1345 #if 0
1346 	else if (p.spt.Cdb[0] == 0x00)	// TEST UNIT READY
1347 	{ unsigned char _sense[18];
1348 
1349 	    operator[](0)   = 0x03;	// REQUEST SENSE
1350 	    p.spt.Cdb[4]    = sizeof(_sense);
1351 	    p.spt.CdbLength = 6;
1352 
1353 	    p.spt.DataBuffer = _sense;
1354 	    p.spt.DataTransferLength = sizeof(_sense);
1355 	    p.spt.DataIn = READ;
1356 
1357 	    if (DeviceIoControl (fd,IOCTL_SCSI_PASS_THROUGH_DIRECT,
1358 				&p,sizeof(p.spt),
1359 				&p,sizeof(p),
1360 				&bytes,FALSE) == 0) return -1;
1361 
1362 	    if ((ret = ERRCODE(_sense))) CREAM_ON_ERRNO(_sense);
1363 	}
1364 #endif
1365 	return ret;
1366     }
1367     int umount (int f=-1)
1368     { DWORD junk;
1369       HANDLE h = (f==-1) ? fd : (HANDLE)f;
1370 
1371 	if (DeviceIoControl(h,FSCTL_LOCK_VOLUME,NULL,0,NULL,0,&junk,NULL) &&
1372 	    DeviceIoControl(h,FSCTL_DISMOUNT_VOLUME,NULL,0,NULL,0,&junk,NULL))
1373 	{   if (h==fd) autoclose++;
1374 	    return 0;
1375 	}
1376 	return -1;
1377     }
1378 #define RELOAD_NEVER_NEEDED
1379     int is_reload_needed (int not_used)
1380     {   return 0;   }
1381 };
1382 
1383 #elif defined(__APPLE__) && defined(__MACH__)
1384 //
1385 // This code targets Darwin Kernel Version 6.x, a.k.a. Mac OS X v10.2,
1386 // or later, but upon initial release was tested only on PowerPC under
1387 // Darwin Kernel Version 8.7.0, a.k.a. Mac OS X v10.4.7 (Tiger).
1388 //
1389 typedef off_t off64_t;
1390 #define stat64   stat
1391 #define fstat64  fstat
1392 #define open64   open
1393 #define pread64  pread
1394 #define pwrite64 pwrite
1395 #define lseek64  lseek
1396 
1397 #include <sys/param.h>
1398 #include <sys/mount.h>
1399 #include <CoreFoundation/CoreFoundation.h>
1400 #include <IOKit/IOKitLib.h>
1401 #include <IOKit/scsi/SCSITaskLib.h>
1402 
1403 static int iokit_err   (IOReturn ioret,SCSITaskStatus stat,
1404 			const unsigned char *sense)
1405 { int ret=-1;
1406 
1407     if      (ioret==kIOReturnSuccess)		ret = 0;
1408     else if (ioret==kIOReturnNoDevice)		errno = ENXIO;
1409     else if (ioret==kIOReturnNoMemory)		errno = ENOMEM;
1410     else if (ioret==kIOReturnExclusiveAccess)	errno = EBUSY;
1411     else					errno = EIO;
1412 
1413     if (ret) return ret;
1414 
1415     if (stat==kSCSITaskStatus_CHECK_CONDITION)
1416     {	ret = ERRCODE(sense);
1417 	if (ret==0)	errno=EIO, ret=-1;
1418 	else		CREAM_ON_ERRNO(sense);
1419     }
1420     else if (stat!=kSCSITaskStatus_GOOD)
1421 	errno = EIO, ret = -1;
1422 
1423   return ret;
1424 }
1425 
1426 // NB! ellipsis is GCC-ism, but conveniently Apple ships only gcc:-)
1427 #define MMCIO(h,func,...)	({			\
1428     MMCDeviceInterface	**di = (MMCDeviceInterface **)h;\
1429     SCSITaskStatus	stat;				\
1430     union {						\
1431 	SCSI_Sense_Data	s;				\
1432 	unsigned char	u[18];				\
1433 	}		sense;				\
1434     IOReturn		ioret;				\
1435     memset (&sense,0,sizeof(sense));			\
1436     ioret = (*di)->func(di,__VA_ARGS__,&stat,&sense.s);	\
1437     iokit_err (ioret,stat,sense.u);			})
1438 
1439 typedef enum {	NONE=kSCSIDataTransfer_NoDataTransfer,
1440 		READ=kSCSIDataTransfer_FromTargetToInitiator,
1441 		WRITE=kSCSIDataTransfer_FromInitiatorToTarget
1442 	     } Direction;
1443 
1444 class Scsi_Command {
1445 private:
1446     int				autoclose,_timeout;
1447     char			*filename;
1448     io_object_t			scsiob;
1449     IOCFPlugInInterface		**plugin;
1450     MMCDeviceInterface		**mmcdif;
1451     SCSITaskDeviceInterface	**taskif;
1452     unsigned char		cdb[16];
1453     union {
1454 	SCSI_Sense_Data		s;
1455 	unsigned char		u[18];
1456     } _sense;
1457     size_t			cdblen,resid;
1458 public:
1459     Scsi_Command()
1460     {	scsiob=IO_OBJECT_NULL, plugin=NULL, mmcdif=NULL, taskif=NULL;
1461 	autoclose=1; filename=NULL;
1462     }
1463     Scsi_Command(void *f)
1464     {	taskif = (SCSITaskDeviceInterface **)f, autoclose=0; filename=NULL;  }
1465     ~Scsi_Command()
1466     {	if (autoclose)
1467 	{   if (taskif)		(*taskif)->ReleaseExclusiveAccess(taskif),
1468 				(*taskif)->Release(taskif),       taskif=NULL;
1469 	    if (mmcdif)		(*mmcdif)->Release(mmcdif),       mmcdif=NULL;
1470 	    if (plugin)		IODestroyPlugInInterface(plugin), plugin=NULL;
1471 	    if (scsiob)		IOObjectRelease(scsiob), scsiob=IO_OBJECT_NULL;
1472 	}
1473 	if (filename)		free(filename), filename=NULL;
1474     }
1475 
1476     int associate (const char *file,const struct stat *ref=NULL)
1477     { struct stat		sb;
1478       io_object_t		scsiob=IO_OBJECT_NULL,parent;
1479       CFMutableDictionaryRef	match,bsddev;
1480       CFNumberRef		num;
1481       int			i;
1482 
1483 	if (ref)			sb = *ref;
1484 	else if (stat(file,&sb))	return 0;
1485 
1486 	if (!(S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)))
1487 	    return !(errno=ENOTBLK);
1488 
1489 	if ((match = CFDictionaryCreateMutable(kCFAllocatorDefault,0,
1490 				&kCFTypeDictionaryKeyCallBacks,
1491 				&kCFTypeDictionaryValueCallBacks))
1492 	    == NULL)	return !(errno=ENOMEM);
1493 	if ((bsddev = CFDictionaryCreateMutable(kCFAllocatorDefault,0,
1494 				&kCFTypeDictionaryKeyCallBacks,
1495 				&kCFTypeDictionaryValueCallBacks))
1496 	    == NULL)	return CFRelease(match),!(errno=ENOMEM);
1497 
1498 	i = major(sb.st_rdev);
1499 	num = CFNumberCreate(kCFAllocatorDefault,kCFNumberIntType,&i);
1500 	CFDictionarySetValue(bsddev,CFSTR("BSD Major"),num);
1501 	CFRelease(num);
1502 
1503 	i = minor(sb.st_rdev);
1504 	num = CFNumberCreate(kCFAllocatorDefault,kCFNumberIntType,&i);
1505 	CFDictionarySetValue(bsddev,CFSTR("BSD Minor"),num);
1506 	CFRelease(num);
1507 
1508 	CFDictionarySetValue(match,CFSTR(kIOPropertyMatchKey),bsddev);
1509 	CFRelease(bsddev);
1510 
1511 	if ((scsiob = IOServiceGetMatchingService(kIOMasterPortDefault,match))
1512 	    == IO_OBJECT_NULL)	return !(errno=ENXIO);
1513 
1514 	// traverse up to "SCSITaskAuthoringDevice"
1515 	kern_return_t	kret;
1516 	while ((kret=IORegistryEntryGetParentEntry(scsiob,kIOServicePlane,
1517 				&parent)) == kIOReturnSuccess)
1518 	{ CFStringRef	uclient;
1519 	  const char	*s;
1520 	  int		cmp;
1521 
1522 	    IOObjectRelease(scsiob);
1523 	    scsiob = parent;
1524 	    uclient = (CFStringRef)IORegistryEntryCreateCFProperty(scsiob,
1525 				CFSTR(kIOPropertySCSITaskDeviceCategory),
1526 				kCFAllocatorDefault,0);
1527 	    if (uclient)
1528 	    {	s = CFStringGetCStringPtr(uclient,kCFStringEncodingMacRoman);
1529 		cmp = strcmp(s,kIOPropertySCSITaskAuthoringDevice);
1530 		CFRelease(uclient);
1531 		if (cmp==0)	break;
1532 	    }
1533 	}
1534 	if (kret!=kIOReturnSuccess)
1535 	{   if (scsiob!=IO_OBJECT_NULL)	IOObjectRelease(scsiob);
1536 	    return !(errno=ENXIO);
1537 	}
1538 
1539 	SInt32	score=0;
1540 	if (IOCreatePlugInInterfaceForService(scsiob,
1541 				kIOMMCDeviceUserClientTypeID,
1542 				kIOCFPlugInInterfaceID,
1543 				&plugin,&score) != kIOReturnSuccess)
1544 	{   IOObjectRelease(scsiob);
1545 	    return !(errno=ENXIO);
1546 	}
1547 	if ((*plugin)->QueryInterface(plugin,
1548 				CFUUIDGetUUIDBytes(kIOMMCDeviceInterfaceID),
1549 				(void**)&mmcdif) != S_OK)
1550 	{   IODestroyPlugInInterface(plugin),	plugin=NULL;
1551 	    IOObjectRelease(scsiob);
1552 	    return !(errno=ENXIO);
1553 	}
1554 	if ((taskif = (*mmcdif)->GetSCSITaskDeviceInterface(mmcdif)) == NULL)
1555 	{   (*mmcdif)->Release(mmcdif),		mmcdif=NULL;
1556 	    IODestroyPlugInInterface(plugin),	plugin=NULL;
1557 	    IOObjectRelease(scsiob);
1558 	    return !(errno=ENXIO);
1559 	}
1560 
1561 	//
1562 	// Note that in order to ObtainExclusiveAccess no corresponding
1563 	// /dev/[r]diskN may remain open by that time. For reference,
1564 	// acquiring exclusive access temporarily removes BSD block
1565 	// storage device from I/O registry as well as corresponding
1566 	// /dev entries.
1567 	//
1568 	if ((*taskif)->ObtainExclusiveAccess(taskif) != kIOReturnSuccess)
1569 	{   (*taskif)->Release(taskif),		taskif=NULL;
1570 	    (*mmcdif)->Release(mmcdif),		mmcdif=NULL;
1571 	    IODestroyPlugInInterface(plugin),	plugin=NULL;
1572 	    IOObjectRelease(scsiob),		scsiob=IO_OBJECT_NULL;
1573 	    return !(errno=EBUSY);
1574 	}
1575 
1576 	filename=strdup(file);
1577 
1578 	return 1;
1579     }
1580     unsigned char &operator[] (size_t i)
1581     {	if (i==0)
1582 	{   memset (cdb,0,sizeof(cdb));
1583 	    memset (&_sense,0,sizeof(_sense));
1584 	    _timeout = 30;
1585 	}
1586 	cdblen = i+1;
1587 	return cdb[i];
1588     }
1589     unsigned char &operator()(size_t i)	{ return _sense.u[i]; }
1590     unsigned char *sense()		{ return _sense.u;    }
1591     void timeout(int i)			{ _timeout=i;         }
1592     size_t residue()			{ return resid;       }
1593     int transport(Direction dir=NONE,void *buf=NULL,size_t sz=0)
1594     { int		ret=0;
1595       SCSITaskInterface **cmd;
1596       SCSITaskStatus	stat;
1597       UInt64		bytes;
1598       IOVirtualRange	range = { (IOVirtualAddress)buf, sz };
1599 
1600 	resid = sz;
1601 	cmd = (*taskif)->CreateSCSITask(taskif);
1602 	if (cmd==NULL)	return (errno=ENOMEM),-1;
1603 
1604 	(*cmd)->SetCommandDescriptorBlock(cmd,cdb,cdblen);
1605 	(*cmd)->SetScatterGatherEntries(cmd,&range,1,sz,dir);
1606 	(*cmd)->SetTimeoutDuration(cmd,_timeout*1000);
1607 
1608 	if ((*cmd)->ExecuteTaskSync(cmd,&_sense.s,&stat,&bytes)
1609 	    != kIOReturnSuccess)
1610 	    errno=EIO, ret=-1;
1611 	else if (stat==kSCSITaskStatus_GOOD)
1612 	{   resid = sz - bytes;   }
1613 	else if (stat==kSCSITaskStatus_CHECK_CONDITION)
1614 	{   ret = ERRCODE(_sense.u);
1615 	    if (ret==0)	errno=EIO, ret=-1;
1616 	    else	CREAM_ON_ERRNO(_sense.u);
1617 	}
1618 	else
1619 	{   //SCSIServiceResponse resp;
1620 	    //(*taskif)->GetSCSIServiceResponse(taskif,&resp);
1621 	    errno=EIO, ret=-1;
1622 	}
1623 
1624 	(*cmd)->Release(cmd);
1625 
1626       return ret;
1627     }
1628     int umount(int f=-1)
1629     { struct stat	sb;
1630       dev_t		ref;
1631       int		i,n;
1632 
1633 	if (f>=0)
1634 	{   if (fstat (f,&sb))		return -1;
1635 	    if (!S_ISCHR(sb.st_mode))	return errno=ENOTBLK,-1;
1636 	    ref = sb.st_rdev;
1637 	    // /dev/rdiskN and /dev/diskN have same st_rdev
1638 	}
1639 	else
1640 	{ char bsdname [16];
1641 	  CFStringRef devname = (CFStringRef)IORegistryEntrySearchCFProperty (
1642 					scsiob,kIOServicePlane,
1643 					CFSTR("BSD Name"),
1644 					kCFAllocatorDefault,
1645 					kIORegistryIterateRecursively);
1646 	    if (devname==NULL)	return 0;	// already exclusive
1647 
1648 	    sprintf (bsdname,"/dev/%.*s",(int)(sizeof(bsdname)-6),
1649 				CFStringGetCStringPtr (devname,0));
1650 	    CFRelease (devname);
1651 
1652 	    if (stat (bsdname,&sb))			return -1;
1653 	    ref = sb.st_rdev;
1654 	}
1655 
1656 	if ((n=getfsstat (NULL,0,MNT_NOWAIT)) < 0)	return -1;
1657 	n += 4, n *= sizeof(struct statfs);
1658 
1659 	struct statfs *p = (struct statfs *)alloca(n);
1660 	if ((n=getfsstat (p,n,MNT_NOWAIT)) < 0)		return -1;
1661 
1662 	for (i=0;i<n;i++,p++)
1663 	{   if (stat (p->f_mntfromname,&sb)==0 &&
1664 		S_ISBLK(sb.st_mode) &&
1665 		sb.st_rdev==ref)
1666 #if 0	    // looks neat, but causes irritaing popups on console...
1667 		return unmount (p->f_mntonname,0);
1668 #else
1669 	    { int	ret=0,rval;
1670 	      pid_t	pid,rpid;
1671 
1672 		ret = -1;
1673 		if ((pid = fork()) == (pid_t)-1)	return -1;
1674 		if (pid == 0)	// if diskutil will be proven broken,
1675 				// don't allow growisofs to be used as
1676 				// attack vector...
1677 				setuid (getuid ()),
1678 				execl  ("/usr/sbin/diskutil",
1679 					"diskutil","unmount",
1680 					p->f_mntonname,(void*)NULL),
1681 				exit (errno);
1682 		while (1)
1683 		{   rpid = waitpid (pid,&rval,0);
1684 		    if (rpid == (pid_t)-1)
1685 		    {	if (errno==EINTR)	continue;
1686 			else			break;
1687 		    }
1688 		    else if (rpid != pid)
1689 		    {	errno = ECHILD;
1690 			break;
1691 		    }
1692 		    if (WIFEXITED(rval))
1693 		    {	if (WEXITSTATUS(rval) == 0)	ret=0;
1694 			else				errno=EBUSY; // most likely
1695 			break;
1696 		    }
1697 		    else if (WIFSTOPPED(rval) || WIFCONTINUED(rval))
1698 			continue;
1699 		    else
1700 		    {	errno = ENOLINK;	// some phony errno
1701 			break;
1702 		    }
1703 		}
1704 
1705 		// diskutil(8) seem to unmount only volfs-managed
1706 		// media, so I check if it managed to unmount and
1707 		// try the system call if it didn't...
1708 		if (ret==0)
1709 		{ struct statfs fs;
1710 
1711 		    if (statfs (p->f_mntonname,&fs)==0 &&
1712 			!strcmp (fs.f_mntfromname,p->f_mntfromname))
1713 			return unmount (p->f_mntonname,0);
1714 		}
1715 	      return ret;
1716 	    }
1717 #endif
1718 	}
1719 
1720       return 0; // not mounted?
1721     }
1722 #define RELOAD_NEVER_NEEDED
1723     int is_reload_needed(int not_used)
1724     {	return 0;   }
1725 };
1726 
1727 #else
1728 #error "Unsupported OS"
1729 #endif
1730 
1731 #define DUMP_EVENTS 0
1732 static int handle_events (Scsi_Command &cmd)
1733 { unsigned char  event[8];
1734   unsigned short profile=0,started=0;
1735   int err,ret=0;
1736   unsigned int descr;
1737   static unsigned char events=0xFF;	// "All events"
1738 
1739     while (events)
1740     {	cmd[0] = 0x4A;		// GET EVENT
1741 	cmd[1] = 1;		// "Polled"
1742 	cmd[4] = events;
1743 	cmd[8] = sizeof(event);
1744 	cmd[9] = 0;
1745 	if ((err=cmd.transport(READ,event,sizeof(event))))
1746 	{   events=0;
1747 	    sperror ("GET EVENT",err);
1748 	    return ret;
1749 	}
1750 
1751 	events = event[3];
1752 
1753 	if ((event[2]&7) == 0			||
1754 	    (event[0]<<8|event[1]) == 2	||
1755 	    (event[4]&0xF) == 0			)	// No Changes
1756 	    return ret;
1757 
1758 	descr  = event[4]<<24|event[5]<<16|event[6]<<8|event[7];
1759 #if DUMP_EVENTS
1760 	fprintf(stderr,"< %d[%08x],%x >\n",event[2],descr,events);
1761 #endif
1762 
1763 	switch(event[2]&7)
1764 	{   case 0: return ret;			// No [supported] events
1765 	    case 1: ret |= 1<<1;		// Operational Change
1766 		if ((descr&0xFFFF) < 3)
1767 		    goto read_profile;
1768 	    start_unit:
1769 	    	if (!started)
1770 		{   cmd[0]=0x1B;	// START STOP UNIT
1771 		    cmd[4]=1;		// "Start"
1772 		    cmd[5]=0;
1773 		    if ((err=cmd.transport()) && err!=0x62800)
1774 			sperror ("START UNIT",err);
1775 		    started=1, profile=0;
1776 		}
1777 	    read_profile:
1778 		if (!profile)
1779 		{   cmd[0] = 0x46;	// GET CONFIGURATION
1780 		    cmd[8] = sizeof(event);
1781 		    cmd[9] = 0;
1782 		    if (!cmd.transport(READ,event,sizeof(event)))
1783 			profile=event[6]<<8|event[7];
1784 		}
1785 		break;
1786 	    case 2: ret |= 1<<2;		// Power Management
1787 		if (event[5]>1)		// State is other than Active
1788 		    goto start_unit;
1789 		break;
1790 	    case 3: ret |= 1<<3; break;		// External Request
1791 	    case 4: ret |= 1<<4;		// Media
1792 		if (event[5]&2)		// Media in
1793 		    goto  start_unit;
1794 		break;
1795 	    case 5: ret |= 1<<5; break;		// Multiple Initiators
1796 	    case 6:				// Device Busy
1797 		if ((event[4]&0xF)==1 &&	// Timeout occured
1798 		    (event[5]&0x3)!=0)
1799 		{   poll(NULL,0,(descr&0xFFFF)*100+100);
1800 		    cmd[0] = 0;		// TEST UNIT READY
1801 		    cmd[5] = 0;
1802 		    if ((err=cmd.transport()))
1803 			sperror("TEST UNIT READY",err);
1804 		    ret |= 1<<6;
1805 		}
1806 		break;
1807 	    case 7: ret |= 1<<7; break;		// Reserved
1808 	}
1809     }
1810 
1811   return ret;
1812 }
1813 #undef DUMP_EVENTS
1814 
1815 static int wait_for_unit (Scsi_Command &cmd,volatile int *progress=NULL)
1816 { unsigned char *sense=cmd.sense(),sensebuf[18];
1817   int  err;
1818   long msecs=1000;
1819 
1820     while (1)
1821     {	if (msecs > 0) poll(NULL,0,msecs);
1822 	msecs = getmsecs();
1823 	cmd[0] = 0;	// TEST UNIT READY
1824 	cmd[5] = 0;
1825 	if (!(err=cmd.transport ())) break;
1826 	// I wish I could test just for sense.valid, but (at least)
1827 	// hp dvd100i returns 0 in valid bit at this point:-( So I
1828 	// check for all bits...
1829 	if ((sense[0]&0x70)==0)
1830 	{   perror (":-( unable to TEST UNIT READY");
1831 	    return -1;
1832 	}
1833 	else if (sense[12]==0x3A) // doesn't get any further than "no media"
1834 	    return err;
1835 
1836 	while (progress)
1837 	{   if (sense[15]&0x80)
1838 	    {	*progress = sense[16]<<8|sense[17];
1839 		break;
1840 	    }
1841 	    // MMC-3 (draft) specification says that the unit should
1842 	    // return progress indicator in key specific bytes even
1843 	    // in reply to TEST UNIT READY. I.e. as above! But (at
1844 	    // least) hp dvd100i doesn't do that and I have to fetch
1845 	    // it separately:-(
1846 	    cmd[0] = 0x03;	// REQUEST SENSE
1847 	    cmd[4] = sizeof(sensebuf);
1848 	    cmd[5] = 0;
1849 	    if ((err=cmd.transport (READ,sensebuf,sizeof(sensebuf))))
1850 	    {   sperror ("REQUEST SENSE",err);
1851 		return err;
1852 	    }
1853 	    if (sensebuf[15]&0x80)
1854 		*progress = sensebuf[16]<<8|sensebuf[17];
1855 	    break;
1856 	}
1857 	msecs = 1000 - (getmsecs() - msecs);
1858     }
1859 
1860   return 0;
1861 }
1862 
1863 static int pioneer_stop(Scsi_Command &cmd,volatile int *progress=NULL)
1864 { int err;
1865   unsigned char *sense=cmd.sense();
1866 
1867     // Pioneer units tend to just fall through wait_for_unit() and
1868     // report "OP IN PROGRESS" on next non-TEST UNIT READY command.
1869     // This behaviour is explicitly discouraged in MMC, but what one
1870     // can do? Well, whenever appropriate one can deploy STOP UNIT
1871     // as "barrier" command with all units...
1872     while (1)
1873     {	cmd[0] = 0x1B;	// START/STOP UNIT
1874 	cmd[1] = 0x1;	// "IMMED"
1875 	cmd[4] = 0;	// "Stop"
1876 	cmd[5] = 0;
1877 	if ((err=cmd.transport()) == 0x20407)	// "OP IN PROGRESS"
1878 	{   if (progress && sense[15]&0x80)
1879 		*progress = sense[16]<<8|sense[17];
1880 	    poll (NULL,0,333);
1881 	    continue;
1882 	}
1883 	break;
1884     }
1885 
1886   return err;
1887 }
1888 
1889 
1890 #define FEATURE21_BROKEN 1
1891 static void page05_setup (Scsi_Command &cmd, unsigned short profile=0,
1892 	unsigned char p32=0xC0)
1893 	// 5 least significant bits of p32 go to p[2], Test Write&Write Type
1894 	// 2 most significant bits go to p[3], Multi-session field
1895 	// 0xC0 means "Multi-session, no Test Write, Incremental"
1896 { unsigned int   len,bdlen;
1897   unsigned char  header[12],track[32],*p;
1898 #if !FEATURE21_BROKEN
1899   unsigned char  feature21[24];
1900 #endif
1901   int            err;
1902   class autofree page05;
1903 
1904     if (profile==0)
1905     { unsigned char prof[8];
1906 
1907 	cmd[0] = 0x46;	// GET CONFIGURATION
1908 	cmd[8] = sizeof(prof);
1909 	cmd[9] = 0;
1910 	if ((err=cmd.transport(READ,prof,sizeof(prof))))
1911 	    sperror ("GET CONFIGURATION",err), exit(FATAL_START(errno));
1912 
1913 	profile = prof[6]<<8|prof[7];
1914     }
1915 
1916 #if !FEATURE21_BROKEN
1917     if (profile==0x11 || profile==0x14)
1918     {	cmd[0] = 0x46;	// GET CONFIGURATION
1919 	cmd[1] = 2;	// ask for the only feature...
1920 	cmd[3] = 0x21;	// the "Incremental Streaming Writable" one
1921 	cmd[8] = 8;	// read the header only to start with
1922 	cmd[9] = 0;
1923 	if ((err=cmd.transport(READ,feature21,8)))
1924 	    sperror ("GET CONFIGURATION",err), exit(FATAL_START(errno));
1925 
1926 	len = feature21[0]<<24|feature21[1]<<16|feature21[2]<<8|feature21[3];
1927 	len += 4;
1928 	if (len>sizeof(feature21))
1929 	    len = sizeof(feature21);
1930 	else if (len<(8+8))
1931 	    fprintf (stderr,":-( READ FEATURE DESCRIPTOR 0021h: insane length\n"),
1932 	    exit(FATAL_START(EINVAL));
1933 
1934 	cmd[0] = 0x46;	// GET CONFIGURATION
1935 	cmd[1] = 2;	// ask for the only feature...
1936 	cmd[3] = 0x21;	// the "Incremental Streaming Writable" one
1937 	cmd[8] = len;	// this time with real length
1938 	cmd[9] = 0;
1939 	if ((err=cmd.transport(READ,feature21,len)))
1940 	    sperror ("READ FEATURE DESCRIPTOR 0021h",err),
1941 	    exit(FATAL_START(errno));
1942 
1943 	if ((feature21[8+2]&1)==0)
1944 	    fprintf (stderr,":-( FEATURE 0021h is not in effect\n"),
1945 	    exit(FATAL_START(EMEDIUMTYPE));
1946     }
1947     else
1948 	feature21[8+2]=0;
1949 #endif
1950 
1951     cmd[0] = 0x52;	// READ TRACK INFORMATION
1952     cmd[1] = 1;		// TRACK INFORMATION
1953     cmd[5] = 1;		// track#1, in DVD context it's safe to assume
1954     			//          that all tracks are in the same mode
1955     cmd[8] = sizeof(track);
1956     cmd[9] = 0;
1957     if ((err=cmd.transport(READ,track,sizeof(track))))
1958 	sperror ("READ TRACK INFORMATION",err), exit(FATAL_START(errno));
1959 
1960     // WRITE PAGE SETUP //
1961     cmd[0] = 0x5A;		// MODE SENSE
1962     cmd[1] = 0x08;		// "Disable Block Descriptors"
1963     cmd[2] = 0x05;		// "Write Page"
1964     cmd[8] = sizeof(header);	// header only to start with
1965     cmd[9] = 0;
1966     if ((err=cmd.transport(READ,header,sizeof(header))))
1967 	sperror ("MODE SENSE",err), exit(FATAL_START(errno));
1968 
1969     len   = (header[0]<<8|header[1])+2;
1970     bdlen = header[6]<<8|header[7];
1971 
1972     if (bdlen)	// should never happen as we set "DBD" above
1973     {	if (len <= (8+bdlen+14))
1974 	    fprintf (stderr,":-( LUN is impossible to bear with...\n"),
1975 	    exit(FATAL_START(EINVAL));
1976     }
1977     else if (len < (8+2+(unsigned int)header[9]))// SANYO does this.
1978 	len = 8+2+header[9];
1979 
1980     page05 = (unsigned char *)malloc(len);
1981     if (page05 == NULL)
1982 	fprintf (stderr,":-( memory exhausted\n"),
1983 	exit(FATAL_START(ENOMEM));
1984 
1985     cmd[0] = 0x5A;		// MODE SENSE
1986     cmd[1] = 0x08;		// "Disable Block Descriptors"
1987     cmd[2] = 0x05;		// "Write Page"
1988     cmd[7] = len>>8;
1989     cmd[8] = len;		// real length this time
1990     cmd[9] = 0;
1991     if ((err=cmd.transport(READ,page05,len)))
1992 	sperror("MODE SENSE",err), exit(FATAL_START(errno));
1993 
1994     len -= 2;
1995     if (len < ((unsigned int)page05[0]<<8|page05[1]))	// paranoia:-)
1996 	page05[0] = len>>8, page05[1] = len;
1997 
1998     len   = (page05[0]<<8|page05[1])+2;
1999     bdlen = page05[6]<<8|page05[7];
2000     len  -= bdlen;
2001     if (len < (8+14))
2002 	fprintf (stderr,":-( LUN is impossible to bear with...\n"),
2003 	exit(FATAL_START(EINVAL));
2004 
2005     p = page05 + 8 + bdlen;
2006 
2007     memset (p-8,0,8);
2008     p[0] &= 0x7F;
2009 
2010     // copy "Test Write" and "Write Type" from p32
2011     p[2] &= ~0x1F, p[2] |= p32&0x1F;
2012     p[2] |= 0x40;	// insist on BUFE on
2013 
2014     // setup Preferred Link Size
2015 #if !FEATURE21_BROKEN
2016     if (feature21[8+2]&1)
2017     {	if (feature21[8+7])	p[2] |= 0x20,  p[5] = feature21[8+8];
2018 	else			p[2] &= ~0x20, p[5] = 0;
2019     }
2020 #else	// At least Pioneer DVR-104 returns some bogus data in
2021 	// Preferred Link Size...
2022     if (profile==0x11 || profile==0x14)	// Sequential recordings...
2023 	p[2] |= 0x20, p[5] = 0x10;
2024 #endif
2025     else
2026 	p[2] &= ~0x20, p[5] = 0;
2027 
2028     // copy Track Mode from TRACK INFORMATION
2029     // [some DVD-R units (most notably Panasonic LF-D310), insist
2030     // on Track Mode 5, even though it's effectively ignored]
2031     p[3] &= ~0x0F, p[3] |= profile==0x11?5:(track[5]&0x0F);
2032 
2033     // copy "Multi-session" bits from p32
2034     p[3] &= ~0xC0, p[3] |= p32&0xC0;
2035     if (profile == 0x13)	// DVD-RW Restricted Overwrite
2036     	p[3] &= 0x3F;		// always Single-session?
2037 
2038     // setup Data Block Type
2039     // Some units [e.g. Toshiba/Samsung TS-H542A] return "unknown Data
2040     // Block Type" in track[6]&0x0F field. Essentially it's a firmware
2041     // glitch, yet it makes certain sense, as track may not be written
2042     // yet...
2043     if ((track[6]&0x0F)==1 || (track[6]&0x0F)==0x0F)
2044 		p[4] = 8;
2045     else	fprintf (stderr,":-( none Mode 1 track\n"),
2046 		exit(FATAL_START(EMEDIUMTYPE));
2047 
2048     // setup Packet Size
2049     // [some DVD-R units (most notably Panasonic LF-D310), insist
2050     // on fixed Packet Size of 16 blocks, even though it's effectively
2051     // ignored]
2052     p[3] |= 0x20, memset (p+10,0,4), p[13] = 0x10;
2053     if (track[6]&0x10)
2054 	memcpy (p+10,track+20,4);	// Fixed
2055     else if (profile != 0x11)
2056 	p[3] &= ~0x20, p[13] = 0;	// Variable
2057 
2058     switch (profile)
2059     {	case 0x13:	// DVD-RW Restricted Overwrite
2060 	    if (!(track[6]&0x10))
2061 		fprintf (stderr,":-( track is not formatted for fixed packet size\n"),
2062 		exit(FATAL_START(EMEDIUMTYPE));
2063 	    break;
2064 	case 0x14:	// DVD-RW Sequential Recording
2065 	case 0x11:	// DVD-R  Sequential Recording
2066 	    if (track[6]&0x10)
2067 		fprintf (stderr,":-( track is formatted for fixed packet size\n"),
2068 		exit(FATAL_START(EMEDIUMTYPE));
2069 	    break;
2070 	default:
2071 #if 0
2072 	    fprintf (stderr,":-( invalid profile %04xh\n",profile);
2073 	    exit(FATAL_START(EMEDIUMTYPE));
2074 #endif
2075 	    break;
2076     }
2077 
2078     p[8] = 0;		// "Session Format" should be ignored, but
2079 			// I reset it just in case...
2080 
2081     cmd[0] = 0x55;	// MODE SELECT
2082     cmd[1] = 0x10;	// conformant
2083     cmd[7] = len>>8;
2084     cmd[8] = len;
2085     cmd[9] = 0;
2086     if ((err=cmd.transport(WRITE,p-8,len)))
2087 	sperror ("MODE SELECT",err), exit(FATAL_START(errno));
2088     // END OF WRITE PAGE SETUP //
2089 }
2090 #undef FEATURE21_BROKEN
2091 
2092 #undef ERRCODE
2093 #undef CREAM_ON_ERRNO
2094 #undef CREAM_ON_ERRNO_NAKED
2095