git-fixes.patch 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778
  1. diff --git a/data/80-udisks2.rules b/data/80-udisks2.rules
  2. index ed093ee..7112db8 100644
  3. --- a/data/80-udisks2.rules
  4. +++ b/data/80-udisks2.rules
  5. @@ -94,6 +94,9 @@ SUBSYSTEMS=="usb", ENV{ID_VENDOR}=="*SanDisk*", ENV{ID_MODEL}=="*Cruzer*", ENV{I
  6. SUBSYSTEMS=="usb", ENV{ID_VENDOR}=="HP", ENV{ID_MODEL}=="*v125w*", ENV{ID_DRIVE_THUMB}="1"
  7. SUBSYSTEMS=="usb", ENV{ID_VENDOR_ID}=="13fe", ENV{ID_MODEL}=="*Patriot*", ENV{ID_DRIVE_THUMB}="1"
  8. +# SD-Card reader in Chromebook Pixel
  9. +SUBSYSTEMS=="usb", ENV{ID_VENDOR_ID}=="05e3", ENV{ID_MODEL_ID}=="0727", ENV{ID_DRIVE_FLASH_SD}="1"
  10. +
  11. # ------------------------------------------------------------------------
  12. # ------------------------------------------------------------------------
  13. # ------------------------------------------------------------------------
  14. diff --git a/doc/man/udisksctl.xml b/doc/man/udisksctl.xml
  15. index 8f38479..434a8fb 100644
  16. --- a/doc/man/udisksctl.xml
  17. +++ b/doc/man/udisksctl.xml
  18. @@ -104,6 +104,16 @@
  19. <cmdsynopsis>
  20. <command>udisksctl</command>
  21. + <arg choice="plain">power-off </arg>
  22. + <group choice="req">
  23. + <arg choice="plain">--object-path <replaceable>OBJECT</replaceable></arg>
  24. + <arg choice="plain">--block-device <replaceable>DEVICE</replaceable></arg>
  25. + </group>
  26. + <arg choice="opt">--no-user-interaction</arg>
  27. + </cmdsynopsis>
  28. +
  29. + <cmdsynopsis>
  30. + <command>udisksctl</command>
  31. <arg choice="plain">smart-simulate </arg>
  32. <arg choice="plain">--file <replaceable>PATH</replaceable></arg>
  33. <group choice="req">
  34. @@ -238,6 +248,31 @@
  35. </varlistentry>
  36. <varlistentry>
  37. + <term><option>power-off</option></term>
  38. + <listitem>
  39. + <para>
  40. + Arranges for the drive to be safely removed and powered
  41. + off. On the OS side this includes ensuring that no process
  42. + is using the drive, then requesting that in-flight buffers
  43. + and caches are committed to stable storage. The exact
  44. + steps for powering off the drive depends on the drive
  45. + itself and the interconnect used. For drives connected
  46. + through USB, the effect is that the USB device will be
  47. + deconfigured followed by disabling the upstream hub port
  48. + it is connected to.
  49. + </para>
  50. + <para>
  51. + Note that as some physical devices contain multiple drives
  52. + (for example 4-in-1 flash card reader USB devices)
  53. + powering off one drive may affect other drives. As such
  54. + there are not a lot of guarantees associated with
  55. + performing this action. Usually the effect is that the
  56. + drive disappears as if it was unplugged.
  57. + </para>
  58. + </listitem>
  59. + </varlistentry>
  60. +
  61. + <varlistentry>
  62. <term><option>smart-simulate</option></term>
  63. <listitem>
  64. <para>
  65. diff --git a/src/udisksdaemonutil.c b/src/udisksdaemonutil.c
  66. index 574bf2c..a588580 100644
  67. --- a/src/udisksdaemonutil.c
  68. +++ b/src/udisksdaemonutil.c
  69. @@ -830,7 +830,7 @@ udisks_daemon_util_get_caller_uid_sync (UDisksDaemon *daemon,
  70. {
  71. struct passwd pwstruct;
  72. gchar pwbuf[8192];
  73. - static struct passwd *pw;
  74. + struct passwd *pw = NULL;
  75. int rc;
  76. rc = getpwuid_r (uid, &pwstruct, pwbuf, sizeof pwbuf, &pw);
  77. @@ -840,6 +840,7 @@ udisks_daemon_util_get_caller_uid_sync (UDisksDaemon *daemon,
  78. UDISKS_ERROR,
  79. UDISKS_ERROR_FAILED,
  80. "User with uid %d does not exist", (gint) uid);
  81. + goto out;
  82. }
  83. else if (pw == NULL)
  84. {
  85. diff --git a/src/udiskslinuxblock.c b/src/udiskslinuxblock.c
  86. index d619850..22bcfd0 100644
  87. --- a/src/udiskslinuxblock.c
  88. +++ b/src/udiskslinuxblock.c
  89. @@ -804,12 +804,23 @@ udisks_linux_block_update (UDisksLinuxBlock *block,
  90. gchar *dm_name_dev_file = NULL;
  91. const gchar *dm_name_dev_file_as_symlink = NULL;
  92. + const gchar *dm_vg_name;
  93. + const gchar *dm_lv_name;
  94. + gchar *dm_lvm_dev_file = NULL;
  95. +
  96. dm_name = g_udev_device_get_property (device->udev_device, "DM_NAME");
  97. if (dm_name != NULL)
  98. dm_name_dev_file = g_strdup_printf ("/dev/mapper/%s", dm_name);
  99. +
  100. + dm_vg_name = g_udev_device_get_property (device->udev_device, "DM_VG_NAME");
  101. + dm_lv_name = g_udev_device_get_property (device->udev_device, "DM_LV_NAME");
  102. + if (dm_vg_name != NULL && dm_lv_name != NULL)
  103. + dm_lvm_dev_file = g_strdup_printf ("/dev/%s/%s", dm_vg_name, dm_lv_name);
  104. +
  105. for (n = 0; symlinks != NULL && symlinks[n] != NULL; n++)
  106. {
  107. - if (g_str_has_prefix (symlinks[n], "/dev/vg_"))
  108. + if (g_str_has_prefix (symlinks[n], "/dev/vg_")
  109. + || g_strcmp0 (symlinks[n], dm_lvm_dev_file) == 0)
  110. {
  111. /* LVM2 */
  112. preferred_device_file = symlinks[n];
  113. @@ -824,6 +835,7 @@ udisks_linux_block_update (UDisksLinuxBlock *block,
  114. if (preferred_device_file == NULL && dm_name_dev_file_as_symlink != NULL)
  115. preferred_device_file = dm_name_dev_file_as_symlink;
  116. g_free (dm_name_dev_file);
  117. + g_free (dm_lvm_dev_file);
  118. }
  119. else if (g_str_has_prefix (device_file, "/dev/md"))
  120. {
  121. diff --git a/src/udiskslinuxdevice.c b/src/udiskslinuxdevice.c
  122. index 0b65a69..8c4a3ed 100644
  123. --- a/src/udiskslinuxdevice.c
  124. +++ b/src/udiskslinuxdevice.c
  125. @@ -199,6 +199,7 @@ probe_ata (UDisksLinuxDevice *device,
  126. {
  127. /* ATA8: 7.16 IDENTIFY DEVICE - ECh, PIO Data-In */
  128. input.command = 0xec;
  129. + input.count = 1;
  130. output.buffer = g_new0 (guchar, 512);
  131. output.buffer_size = 512;
  132. if (!udisks_ata_send_command_sync (fd,
  133. @@ -221,6 +222,7 @@ probe_ata (UDisksLinuxDevice *device,
  134. {
  135. /* ATA8: 7.17 IDENTIFY PACKET DEVICE - A1h, PIO Data-In */
  136. input.command = 0xa1;
  137. + input.count = 1;
  138. output.buffer = g_new0 (guchar, 512);
  139. output.buffer_size = 512;
  140. if (!udisks_ata_send_command_sync (fd,
  141. diff --git a/src/udiskslinuxdrive.c b/src/udiskslinuxdrive.c
  142. index 170ba27..ed541ff 100644
  143. --- a/src/udiskslinuxdrive.c
  144. +++ b/src/udiskslinuxdrive.c
  145. @@ -25,6 +25,12 @@
  146. #include <sys/stat.h>
  147. #include <sys/ioctl.h>
  148. #include <fcntl.h>
  149. +#include <inttypes.h>
  150. +#include <errno.h>
  151. +#include <linux/bsg.h>
  152. +#include <scsi/scsi.h>
  153. +#include <scsi/sg.h>
  154. +#include <scsi/scsi_ioctl.h>
  155. #include <pwd.h>
  156. #include <grp.h>
  157. @@ -1192,6 +1198,122 @@ handle_set_configuration (UDisksDrive *_drive,
  158. /* ---------------------------------------------------------------------------------------------------- */
  159. +/* TODO: move to udisksscsi.[ch] similar what we do for ATA with udisksata.[ch] */
  160. +
  161. +static gboolean
  162. +send_scsi_command_sync (gint fd,
  163. + guint8 *cdb,
  164. + gsize cdb_len,
  165. + GError **error)
  166. +{
  167. + struct sg_io_v4 io_v4;
  168. + uint8_t sense[32];
  169. + gboolean ret = FALSE;
  170. + gint rc;
  171. + gint timeout_msec = 30000; /* 30 seconds */
  172. +
  173. + g_return_val_if_fail (fd != -1, FALSE);
  174. + g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
  175. +
  176. + /* See http://sg.danny.cz/sg/sg_io.html and http://www.tldp.org/HOWTO/SCSI-Generic-HOWTO/index.html
  177. + * for detailed information about how the SG_IO ioctl work
  178. + */
  179. +
  180. + memset (sense, 0, sizeof (sense));
  181. + memset (&io_v4, 0, sizeof (io_v4));
  182. + io_v4.guard = 'Q';
  183. + io_v4.protocol = BSG_PROTOCOL_SCSI;
  184. + io_v4.subprotocol = BSG_SUB_PROTOCOL_SCSI_CMD;
  185. + io_v4.request_len = cdb_len;
  186. + io_v4.request = (uintptr_t) cdb;
  187. + io_v4.max_response_len = sizeof (sense);
  188. + io_v4.response = (uintptr_t) sense;
  189. + io_v4.timeout = timeout_msec;
  190. +
  191. + rc = ioctl (fd, SG_IO, &io_v4);
  192. + if (rc != 0)
  193. + {
  194. + /* could be that the driver doesn't do version 4, try version 3 */
  195. + if (errno == EINVAL)
  196. + {
  197. + struct sg_io_hdr io_hdr;
  198. + memset (&io_hdr, 0, sizeof (struct sg_io_hdr));
  199. + io_hdr.interface_id = 'S';
  200. + io_hdr.cmdp = (unsigned char*) cdb;
  201. + io_hdr.cmd_len = cdb_len;
  202. + io_hdr.dxfer_direction = SG_DXFER_NONE;
  203. + io_hdr.sbp = sense;
  204. + io_hdr.mx_sb_len = sizeof (sense);
  205. + io_hdr.timeout = timeout_msec;
  206. +
  207. + rc = ioctl (fd, SG_IO, &io_hdr);
  208. + if (rc != 0)
  209. + {
  210. + g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno),
  211. + "SGIO v3 ioctl failed (v4 not supported): %m");
  212. + goto out;
  213. + }
  214. + else
  215. + {
  216. + if (!(io_hdr.status == 0 &&
  217. + io_hdr.host_status == 0 &&
  218. + io_hdr.driver_status == 0))
  219. + {
  220. + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
  221. + "Non-GOOD SCSI status from SGIO v3 ioctl: "
  222. + "status=%d host_status=%d driver_status=%d",
  223. + io_hdr.status,
  224. + io_hdr.host_status,
  225. + io_hdr.driver_status);
  226. + goto out;
  227. + }
  228. + }
  229. + }
  230. + else
  231. + {
  232. + g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno),
  233. + "SGIO v4 ioctl failed: %m");
  234. + goto out;
  235. + }
  236. + }
  237. + else
  238. + {
  239. + if (!(io_v4.device_status == 0 &&
  240. + io_v4.transport_status == 0 &&
  241. + io_v4.driver_status == 0))
  242. + {
  243. + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
  244. + "Non-GOOD SCSI status from SGIO v4 ioctl: "
  245. + "device_status=%d transport_status=%d driver_status=%d",
  246. + io_v4.device_status,
  247. + io_v4.transport_status,
  248. + io_v4.driver_status);
  249. + goto out;
  250. + }
  251. + }
  252. +
  253. + ret = TRUE;
  254. +
  255. + out:
  256. + return ret;
  257. +}
  258. +
  259. +static gboolean
  260. +send_scsi_start_stop_command_sync (gint fd,
  261. + GError **error)
  262. +{
  263. + uint8_t cdb[6];
  264. +
  265. + /* SBC3 (SCSI Block Commands), 5.20 START STOP UNIT command
  266. + */
  267. + memset (cdb, 0, sizeof cdb);
  268. + cdb[0] = 0x1b; /* OPERATION CODE: START STOP UNIT */
  269. +
  270. + return send_scsi_command_sync (fd, cdb, sizeof cdb, error);
  271. +}
  272. +
  273. +/* ---------------------------------------------------------------------------------------------------- */
  274. +
  275. static gboolean
  276. handle_power_off (UDisksDrive *_drive,
  277. GDBusMethodInvocation *invocation,
  278. @@ -1216,6 +1338,7 @@ handle_power_off (UDisksDrive *_drive,
  279. gid_t caller_gid;
  280. pid_t caller_pid;
  281. GList *sibling_objects = NULL, *l;
  282. + gint fd = -1;
  283. object = udisks_daemon_util_dup_object (drive, &error);
  284. if (object == NULL)
  285. @@ -1324,10 +1447,10 @@ handle_power_off (UDisksDrive *_drive,
  286. {
  287. UDisksBlock *block_to_sync = UDISKS_BLOCK (l->data);
  288. const gchar *device_file;
  289. - gint fd;
  290. + gint device_fd;
  291. device_file = udisks_block_get_device (block_to_sync);
  292. - fd = open (device_file, O_RDONLY|O_NONBLOCK|O_EXCL);
  293. - if (fd == -1)
  294. + device_fd = open (device_file, O_RDONLY|O_NONBLOCK|O_EXCL);
  295. + if (device_fd == -1)
  296. {
  297. g_dbus_method_invocation_return_error (invocation,
  298. UDISKS_ERROR,
  299. @@ -1336,7 +1459,7 @@ handle_power_off (UDisksDrive *_drive,
  300. device_file);
  301. goto out;
  302. }
  303. - if (fsync (fd) != 0)
  304. + if (fsync (device_fd) != 0)
  305. {
  306. g_dbus_method_invocation_return_error (invocation,
  307. UDISKS_ERROR,
  308. @@ -1345,7 +1468,7 @@ handle_power_off (UDisksDrive *_drive,
  309. device_file);
  310. goto out;
  311. }
  312. - if (close (fd) != 0)
  313. + if (close (device_fd) != 0)
  314. {
  315. g_dbus_method_invocation_return_error (invocation,
  316. UDISKS_ERROR,
  317. @@ -1356,9 +1479,45 @@ handle_power_off (UDisksDrive *_drive,
  318. }
  319. }
  320. - escaped_device = udisks_daemon_util_escape_and_quote (udisks_block_get_device (block));
  321. + /* Send the "SCSI START STOP UNIT" command to request that the unit
  322. + * be stopped but don't treat failure as fatal. In fact some
  323. + * USB-attached hard-disks fails with this command, probably due to
  324. + * the SCSI/SATA translation layer.
  325. + */
  326. + fd = open (udisks_block_get_device (block), O_RDONLY|O_NONBLOCK|O_EXCL);
  327. + if (fd == -1)
  328. + {
  329. + g_dbus_method_invocation_return_error (invocation,
  330. + UDISKS_ERROR,
  331. + UDISKS_ERROR_FAILED,
  332. + "Error opening %s: %m",
  333. + udisks_block_get_device (block));
  334. + goto out;
  335. + }
  336. + if (!send_scsi_start_stop_command_sync (fd, &error))
  337. + {
  338. + udisks_warning ("Ignoring SCSI command START STOP UNIT failure (%s) on %s",
  339. + error->message,
  340. + udisks_block_get_device (block));
  341. + g_clear_error (&error);
  342. + }
  343. + else
  344. + {
  345. + udisks_notice ("Powering off %s - successfully sent SCSI command START STOP UNIT",
  346. + udisks_block_get_device (block));
  347. + }
  348. + if (close (fd) != 0)
  349. + {
  350. + g_dbus_method_invocation_return_error (invocation,
  351. + UDISKS_ERROR,
  352. + UDISKS_ERROR_FAILED,
  353. + "Error closing %s: %m",
  354. + udisks_block_get_device (block));
  355. + goto out;
  356. + }
  357. + fd = -1;
  358. - /* TODO: Send the eject? Send SCSI START STOP UNIT? */
  359. + escaped_device = udisks_daemon_util_escape_and_quote (udisks_block_get_device (block));
  360. device = udisks_linux_drive_object_get_device (object, TRUE /* get_hw */);
  361. if (device == NULL)
  362. {
  363. @@ -1405,10 +1564,20 @@ handle_power_off (UDisksDrive *_drive,
  364. }
  365. }
  366. fclose (f);
  367. + udisks_notice ("Powered off %s - successfully wrote to sysfs path %s",
  368. + udisks_block_get_device (block),
  369. + remove_path);
  370. udisks_drive_complete_power_off (UDISKS_DRIVE (drive), invocation);
  371. out:
  372. + if (fd != -1)
  373. + {
  374. + if (close (fd) != 0)
  375. + {
  376. + udisks_warning ("Error closing device: %m");
  377. + }
  378. + }
  379. g_list_free_full (blocks_to_sync, g_object_unref);
  380. g_list_free_full (sibling_objects, g_object_unref);
  381. g_free (remove_path);
  382. diff --git a/src/udiskslinuxdriveata.c b/src/udiskslinuxdriveata.c
  383. index 48cc6e6..534ef4d 100644
  384. --- a/src/udiskslinuxdriveata.c
  385. +++ b/src/udiskslinuxdriveata.c
  386. @@ -1943,7 +1943,7 @@ udisks_linux_drive_ata_secure_erase_sync (UDisksLinuxDriveAta *drive,
  387. /* First get the IDENTIFY data directly from the drive, for sanity checks */
  388. {
  389. /* ATA8: 7.16 IDENTIFY DEVICE - ECh, PIO Data-In */
  390. - UDisksAtaCommandInput input = {.command = 0xec};
  391. + UDisksAtaCommandInput input = {.command = 0xec, .count = 1};
  392. UDisksAtaCommandOutput output = {.buffer = identify.buf, .buffer_size = sizeof (identify.buf)};
  393. if (!udisks_ata_send_command_sync (fd,
  394. -1,
  395. diff --git a/src/udiskslinuxfilesystem.c b/src/udiskslinuxfilesystem.c
  396. index 4c8d8aa..f243046 100644
  397. --- a/src/udiskslinuxfilesystem.c
  398. +++ b/src/udiskslinuxfilesystem.c
  399. @@ -348,13 +348,16 @@ find_mount_options_for_fs (const gchar *fstype)
  400. static gid_t
  401. find_primary_gid (uid_t uid)
  402. {
  403. - struct passwd *pw;
  404. + struct passwd *pw = NULL;
  405. + struct passwd pwstruct;
  406. + gchar pwbuf[8192];
  407. + int rc;
  408. gid_t gid;
  409. gid = (gid_t) - 1;
  410. - pw = getpwuid (uid);
  411. - if (pw == NULL)
  412. + rc = getpwuid_r (uid, &pwstruct, pwbuf, sizeof pwbuf, &pw);
  413. + if (rc != 0 || pw == NULL)
  414. {
  415. udisks_warning ("Error looking up uid %d: %m", uid);
  416. goto out;
  417. @@ -370,7 +373,10 @@ is_uid_in_gid (uid_t uid,
  418. gid_t gid)
  419. {
  420. gboolean ret;
  421. - struct passwd *pw;
  422. + struct passwd *pw = NULL;
  423. + struct passwd pwstruct;
  424. + gchar pwbuf[8192];
  425. + int rc;
  426. static gid_t supplementary_groups[128];
  427. int num_supplementary_groups = 128;
  428. int n;
  429. @@ -379,8 +385,8 @@ is_uid_in_gid (uid_t uid,
  430. ret = FALSE;
  431. - pw = getpwuid (uid);
  432. - if (pw == NULL)
  433. + rc = getpwuid_r (uid, &pwstruct, pwbuf, sizeof pwbuf, &pw);
  434. + if (rc != 0 || pw == NULL)
  435. {
  436. udisks_warning ("Error looking up uid %d: %m", uid);
  437. goto out;
  438. diff --git a/src/udisksspawnedjob.c b/src/udisksspawnedjob.c
  439. index 802551f..b181933 100644
  440. --- a/src/udisksspawnedjob.c
  441. +++ b/src/udisksspawnedjob.c
  442. @@ -371,22 +371,25 @@ static void
  443. child_setup (gpointer user_data)
  444. {
  445. UDisksSpawnedJob *job = UDISKS_SPAWNED_JOB (user_data);
  446. - struct passwd *pw;
  447. + struct passwd pwstruct;
  448. + gchar pwbuf[8192];
  449. + struct passwd *pw = NULL;
  450. + int rc;
  451. gid_t egid;
  452. if (job->run_as_uid == getuid () && job->run_as_euid == geteuid ())
  453. goto out;
  454. - pw = getpwuid (job->run_as_euid);
  455. - if (pw == NULL)
  456. + rc = getpwuid_r (job->run_as_euid, &pwstruct, pwbuf, sizeof pwbuf, &pw);
  457. + if (rc != 0 || pw == NULL)
  458. {
  459. g_printerr ("No password record for uid %d: %m\n", (gint) job->run_as_euid);
  460. abort ();
  461. }
  462. egid = pw->pw_gid;
  463. - pw = getpwuid (job->run_as_uid);
  464. - if (pw == NULL)
  465. + rc = getpwuid_r (job->run_as_uid, &pwstruct, pwbuf, sizeof pwbuf, &pw);
  466. + if (rc != 0 || pw == NULL)
  467. {
  468. g_printerr ("No password record for uid %d: %m\n", (gint) job->run_as_uid);
  469. abort ();
  470. diff --git a/tools/udisksctl.c b/tools/udisksctl.c
  471. index 97b0f17..c87fe9f 100644
  472. --- a/tools/udisksctl.c
  473. +++ b/tools/udisksctl.c
  474. @@ -1691,6 +1691,12 @@ handle_command_loop (gint *argc,
  475. goto out;
  476. }
  477. + if (udisks_object_peek_loop (object) == NULL)
  478. + {
  479. + g_printerr ("Error: specified object is not a loop device\n");
  480. + goto out;
  481. + }
  482. +
  483. delete_try_again:
  484. error = NULL;
  485. if (!udisks_loop_call_delete_sync (udisks_object_peek_loop (object),
  486. @@ -2009,6 +2015,238 @@ handle_command_smart_simulate (gint *argc,
  487. /* ---------------------------------------------------------------------------------------------------- */
  488. +static gchar *opt_power_off_object_path = NULL;
  489. +static gchar *opt_power_off_device = NULL;
  490. +static gboolean opt_power_off_no_user_interaction = FALSE;
  491. +
  492. +static const GOptionEntry command_power_off_entries[] =
  493. +{
  494. + {
  495. + "object-path",
  496. + 'p',
  497. + 0,
  498. + G_OPTION_ARG_STRING,
  499. + &opt_power_off_object_path,
  500. + "Object path for ATA device",
  501. + NULL
  502. + },
  503. + {
  504. + "block-device",
  505. + 'b',
  506. + 0,
  507. + G_OPTION_ARG_STRING,
  508. + &opt_power_off_device,
  509. + "Device file for ATA device",
  510. + NULL
  511. + },
  512. + {
  513. + "no-user-interaction",
  514. + 0, /* no short option */
  515. + 0,
  516. + G_OPTION_ARG_NONE,
  517. + &opt_power_off_no_user_interaction,
  518. + "Do not authenticate the user if needed",
  519. + NULL
  520. + },
  521. + {
  522. + NULL
  523. + }
  524. +};
  525. +
  526. +static gint
  527. +handle_command_power_off (gint *argc,
  528. + gchar **argv[],
  529. + gboolean request_completion,
  530. + const gchar *completion_cur,
  531. + const gchar *completion_prev)
  532. +{
  533. + gint ret;
  534. + GOptionContext *o;
  535. + gchar *s;
  536. + gboolean complete_objects;
  537. + gboolean complete_devices;
  538. + GList *l;
  539. + GList *objects;
  540. + UDisksObject *object;
  541. + UDisksDriveAta *ata;
  542. + guint n;
  543. + GVariant *options;
  544. + GVariantBuilder builder;
  545. + GError *error;
  546. +
  547. + ret = 1;
  548. + opt_power_off_object_path = NULL;
  549. + opt_power_off_device = NULL;
  550. + object = NULL;
  551. + options = NULL;
  552. +
  553. + modify_argv0_for_command (argc, argv, "power-off");
  554. +
  555. + o = g_option_context_new (NULL);
  556. + if (request_completion)
  557. + g_option_context_set_ignore_unknown_options (o, TRUE);
  558. + g_option_context_set_help_enabled (o, FALSE);
  559. + g_option_context_set_summary (o, "Safely power off a drive.");
  560. + g_option_context_add_main_entries (o,
  561. + command_power_off_entries,
  562. + NULL /* GETTEXT_PACKAGE*/);
  563. +
  564. + complete_objects = FALSE;
  565. + if (request_completion && (g_strcmp0 (completion_prev, "--object-path") == 0 || g_strcmp0 (completion_prev, "-p") == 0))
  566. + {
  567. + complete_objects = TRUE;
  568. + remove_arg ((*argc) - 1, argc, argv);
  569. + }
  570. +
  571. + complete_devices = FALSE;
  572. + if (request_completion && (g_strcmp0 (completion_prev, "--block-device") == 0 || g_strcmp0 (completion_prev, "-b") == 0))
  573. + {
  574. + complete_devices = TRUE;
  575. + remove_arg ((*argc) - 1, argc, argv);
  576. + }
  577. +
  578. + if (!g_option_context_parse (o, argc, argv, NULL))
  579. + {
  580. + if (!request_completion)
  581. + {
  582. + s = g_option_context_get_help (o, FALSE, NULL);
  583. + g_printerr ("%s", s);
  584. + g_free (s);
  585. + goto out;
  586. + }
  587. + }
  588. +
  589. + if (request_completion)
  590. + {
  591. + if ((opt_power_off_object_path == NULL && !complete_objects) &&
  592. + (opt_power_off_device == NULL && !complete_devices))
  593. + {
  594. + g_print ("--object-path \n"
  595. + "--block-device \n");
  596. + }
  597. +
  598. + if (complete_objects)
  599. + {
  600. + const gchar *object_path;
  601. + objects = g_dbus_object_manager_get_objects (udisks_client_get_object_manager (client));
  602. + for (l = objects; l != NULL; l = l->next)
  603. + {
  604. + object = UDISKS_OBJECT (l->data);
  605. + ata = udisks_object_peek_drive_ata (object);
  606. + if (ata != NULL)
  607. + {
  608. + object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (object));
  609. + g_assert (g_str_has_prefix (object_path, "/org/freedesktop/UDisks2/"));
  610. + g_print ("%s \n", object_path + sizeof ("/org/freedesktop/UDisks2/") - 1);
  611. + }
  612. + }
  613. + g_list_foreach (objects, (GFunc) g_object_unref, NULL);
  614. + g_list_free (objects);
  615. + }
  616. +
  617. + if (complete_devices)
  618. + {
  619. + objects = g_dbus_object_manager_get_objects (udisks_client_get_object_manager (client));
  620. + for (l = objects; l != NULL; l = l->next)
  621. + {
  622. + object = UDISKS_OBJECT (l->data);
  623. + ata = udisks_object_peek_drive_ata (object);
  624. + if (ata != NULL)
  625. + {
  626. + const gchar * const *symlinks;
  627. + UDisksBlock *block;
  628. + block = udisks_client_get_block_for_drive (client, udisks_object_peek_drive (object), TRUE);
  629. + g_print ("%s \n", udisks_block_get_device (block));
  630. + symlinks = udisks_block_get_symlinks (block);
  631. + for (n = 0; symlinks != NULL && symlinks[n] != NULL; n++)
  632. + g_print ("%s \n", symlinks[n]);
  633. + }
  634. + }
  635. + g_list_foreach (objects, (GFunc) g_object_unref, NULL);
  636. + g_list_free (objects);
  637. + }
  638. + goto out;
  639. + }
  640. +
  641. + g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
  642. + if (opt_power_off_no_user_interaction)
  643. + {
  644. + g_variant_builder_add (&builder,
  645. + "{sv}",
  646. + "auth.no_user_interaction", g_variant_new_boolean (TRUE));
  647. + }
  648. + options = g_variant_builder_end (&builder);
  649. + g_variant_ref_sink (options);
  650. +
  651. + if (opt_power_off_object_path != NULL)
  652. + {
  653. + object = lookup_object_by_path (opt_power_off_object_path);
  654. + if (object == NULL)
  655. + {
  656. + g_printerr ("Error looking up object with path %s\n", opt_power_off_object_path);
  657. + goto out;
  658. + }
  659. + }
  660. + else if (opt_power_off_device != NULL)
  661. + {
  662. + UDisksObject *block_object;
  663. + UDisksDrive *drive;
  664. + block_object = lookup_object_by_device (opt_power_off_device);
  665. + if (block_object == NULL)
  666. + {
  667. + g_printerr ("Error looking up object for device %s\n", opt_power_off_device);
  668. + goto out;
  669. + }
  670. + drive = udisks_client_get_drive_for_block (client, udisks_object_peek_block (block_object));
  671. + object = (UDisksObject *) g_dbus_interface_dup_object (G_DBUS_INTERFACE (drive));
  672. + g_object_unref (block_object);
  673. + }
  674. + else
  675. + {
  676. + s = g_option_context_get_help (o, FALSE, NULL);
  677. + g_printerr ("%s", s);
  678. + g_free (s);
  679. + goto out;
  680. + }
  681. +
  682. + try_again:
  683. + error = NULL;
  684. + if (!udisks_drive_call_power_off_sync (udisks_object_peek_drive (object),
  685. + options,
  686. + NULL, /* GCancellable */
  687. + &error))
  688. + {
  689. + if (error->domain == UDISKS_ERROR &&
  690. + error->code == UDISKS_ERROR_NOT_AUTHORIZED_CAN_OBTAIN &&
  691. + setup_local_polkit_agent ())
  692. + {
  693. + g_error_free (error);
  694. + goto try_again;
  695. + }
  696. + g_dbus_error_strip_remote_error (error);
  697. + g_printerr ("Error powering off drive: %s (%s, %d)\n",
  698. + error->message, g_quark_to_string (error->domain), error->code);
  699. + g_clear_error (&error);
  700. + g_object_unref (object);
  701. + goto out;
  702. + }
  703. +
  704. + g_object_unref (object);
  705. +
  706. +
  707. + ret = 0;
  708. +
  709. + out:
  710. + if (options != NULL)
  711. + g_variant_unref (options);
  712. + g_option_context_free (o);
  713. + g_free (opt_power_off_object_path);
  714. + g_free (opt_power_off_device);
  715. + return ret;
  716. +}
  717. +
  718. +/* ---------------------------------------------------------------------------------------------------- */
  719. +
  720. static gchar *opt_info_object = NULL;
  721. static gchar *opt_info_device = NULL;
  722. static gchar *opt_info_drive = NULL;
  723. @@ -2855,6 +3093,7 @@ usage (gint *argc, gchar **argv[], gboolean use_stdout)
  724. " lock Lock an encrypted device\n"
  725. " loop-setup Set-up a loop device\n"
  726. " loop-delete Delete a loop device\n"
  727. + " power-off Safely power off a drive\n"
  728. " smart-simulate Set SMART data for a drive\n"
  729. "\n"
  730. "Use \"%s COMMAND --help\" to get help on each command.\n",
  731. @@ -3053,6 +3292,15 @@ main (int argc,
  732. completion_prev);
  733. goto out;
  734. }
  735. + else if (g_strcmp0 (command, "power-off") == 0)
  736. + {
  737. + ret = handle_command_power_off (&argc,
  738. + &argv,
  739. + request_completion,
  740. + completion_cur,
  741. + completion_prev);
  742. + goto out;
  743. + }
  744. else if (g_strcmp0 (command, "dump") == 0)
  745. {
  746. ret = handle_command_dump (&argc,
  747. @@ -3156,6 +3404,7 @@ main (int argc,
  748. "unlock \n"
  749. "loop-setup \n"
  750. "loop-delete \n"
  751. + "power-off \n"
  752. "smart-simulate \n"
  753. );
  754. ret = 0;