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