hunk.code_window:       <span>Validate write on success</span>
       </label>
     </div>
     <div class="checkbox">
       <label>
         <input type="checkbox"
           tabindex="9"
  
hunk.labels: keep keep keep add keep keep keep keep
hunk.after_edit:   <div class="checkbox">
       <label>
         <input type="checkbox"
           tabindex="8"
           ng-model="settings.currentData.trim"
           ng-change="settings.toggle('trim')">
         <span>Trim ext{2,3,4} partitions before writing (raw images only)</span>
       </label>
     </div>
  
hunk.file_path: lib/gui/app/pages/settings/templates/settings.tpl.html
hunk.type: add
file: /*
   * Copyright 2017 resin.io
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *    http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  /* eslint-disable lodash/prefer-lodash-method,quotes,no-magic-numbers,require-jsdoc */
  
  'use strict'
  
  const Bluebird = require('bluebird')
  const childProcess = Bluebird.promisifyAll(require('child_process'))
  const fs = require('fs')
  const _ = require('lodash')
  const os = require('os')
  const sudoPrompt = Bluebird.promisifyAll(require('sudo-prompt'))
  const { promisify } = require('util')
  
  const errors = require('./errors')
  
  const { tmpFileDisposer } = require('./utils')
  
  const writeFileAsync = promisify(fs.writeFile)
  
  /**
   * @summary The user id of the UNIX "superuser"
   * @constant
   * @type {Number}
   */
  const UNIX_SUPERUSER_USER_ID = 0
  
  /**
   * @summary Check if the current process is running with elevated permissions
   * @function
   * @public
   *
   * @description
   * This function has been adapted from https://github.com/sindresorhus/is-elevated,
   * which was originally licensed under MIT.
   *
   * We're not using such module directly given that it
   * contains dependencies with dynamic undeclared dependencies,
   * causing a mess when trying to concatenate the code.
   *
   * @fulfil {Boolean} - whether the current process has elevated permissions
   * @returns {Promise}
   *
   * @example
   * permissions.isElevated().then((isElevated) => {
   *   if (isElevated) {
   *     console.log('This process has elevated permissions');
   *   }
   * });
   */
  exports.isElevated = () => {
    if (os.platform() === 'win32') {
      // `fltmc` is available on WinPE, XP, Vista, 7, 8, and 10
      // Works even when the "Server" service is disabled
      // See http://stackoverflow.com/a/28268802
      return childProcess.execAsync('fltmc')
        .then(_.constant(true))
        .catch({
          code: os.constants.errno.EPERM
        }, _.constant(false))
    }
  
    return Bluebird.resolve(process.geteuid() === UNIX_SUPERUSER_USER_ID)
  }
  
  /**
   * @summary Check if the current process is running with elevated permissions
   * @function
   * @public
   *
   * @description
   *
   * @returns {Boolean}
   *
   * @example
   * permissions.isElevatedUnixSync()
   *   if (isElevated) {
   *     console.log('This process has elevated permissions');
   *   }
   * });
   */
  exports.isElevatedUnixSync = () => {
    return (process.geteuid() === UNIX_SUPERUSER_USER_ID)
  }
  
  const escapeSh = (value) => {
    // Make sure it's a string
    // Replace ' -> '\'' (closing quote, escaped quote, opening quote)
    // Surround with quotes
    return `'${String(value).replace(/'/g, "'\\''")}'`
  }
  
  const escapeParamCmd = (value) => {
    // Make sure it's a string
    // Escape " -> \"
    // Surround with double quotes
    return `"${String(value).replace(/"/g, '\\"')}"`
  }
  
  const setEnvVarSh = (value, name) => {
    return `export ${name}=${escapeSh(value)}`
  }
  
  const setEnvVarCmd = (value, name) => {
    return `set "${name}=${String(value)}"`
  }
  
  // Exported for tests
  exports.createLaunchScript = (command, argv, environment) => {
    const isWindows = os.platform() === 'win32'
    const lines = []
    if (isWindows) {
      // Switch to utf8
      lines.push('chcp 65001')
    }
    const [ setEnvVarFn, escapeFn ] = isWindows ? [ setEnvVarCmd, escapeParamCmd ] : [ setEnvVarSh, escapeSh ]
    lines.push(..._.map(environment, setEnvVarFn))
    lines.push([ command, ...argv ].map(escapeFn).join(' '))
    return lines.join(os.EOL)
  }
  
  const elevateScriptWindows = async (path) => {
    // './nativeModule' imported here as it only exists on windows
    // TODO: replace this with sudo-prompt once https://github.com/jorangreef/sudo-prompt/issues/96 is fixed
    const nativeModule = require('./native-module')
    const elevateAsync = promisify(nativeModule.load('elevator').elevate)
  
    // '&' needs to be escaped here (but not when written to a .cmd file)
    const cmd = [ 'cmd', '/c', escapeParamCmd(path).replace(/&/g, '^&') ]
    const { cancelled } = await elevateAsync(cmd)
    return { cancelled }
  }
  
  const elevateScriptUnix = async (path, name) => {
    const cmd = [ 'sh', escapeSh(path) ].join(' ')
    const [ , stderr ] = await sudoPrompt.execAsync(cmd, { name })
    if (!_.isEmpty(stderr)) {
      throw errors.createError({ title: stderr })
    }
    return { cancelled: false }
  }
  
  /**
   * @summary Elevate a command
   * @function
   * @public
   *
   * @param {String[]} command - command arguments
   * @param {Object} options - options
   * @param {String} options.applicationName - application name
   * @param {Object} options.environment - environment variables
   * @fulfil {Object} - elevation results
   * @returns {Promise}
   *
   * @example
   * permissions.elevateCommand([ 'foo', 'bar' ], {
   *   applicationName: 'My App',
   *   environment: {
   *     FOO: 'bar'
   *   }
   * }).then((results) => {
   *   if (results.cancelled) {
   *     console.log('Elevation has been cancelled');
   *   }
   * });
   */
  exports.elevateCommand = async (command, options) => {
    if (await exports.isElevated()) {
      await childProcess.execFileAsync(command[0], command.slice(1), { env: options.environment })
      return { cancelled: false }
    }
    const isWindows = os.platform() === 'win32'
    const launchScript = exports.createLaunchScript(command[0], command.slice(1), options.environment)
    return Bluebird.using(tmpFileDisposer({ postfix: '.cmd' }), async ({ path }) => {
      await writeFileAsync(path, launchScript)
      if (isWindows) {
        return elevateScriptWindows(path)
      }
      try {
        return await elevateScriptUnix(path, options.applicationName)
      } catch (error) {
        // We're hardcoding internal error messages declared by `sudo-prompt`.
        // There doesn't seem to be a better way to handle these errors, so
        // for now, we should make sure we double check if the error messages
        // have changed every time we upgrade `sudo-prompt`.
        console.log('error', error)
        if (_.includes(error.message, 'is not in the sudoers file')) {
          throw errors.createUserError({
            title: "Your user doesn't have enough privileges to proceed",
            description: 'This application requires sudo privileges to be able to write to drives'
          })
        } else if (_.startsWith(error.message, 'Command failed:')) {
          throw errors.createUserError({
            title: 'The elevated process died unexpectedly',
            description: `The process error code was ${error.code}`
          })
        } else if (error.message === 'User did not grant permission.') {
          return { cancelled: true }
        } else if (error.message === 'No polkit authentication agent found.') {
          throw errors.createUserError({
            title: 'No polkit authentication agent found',
            description: 'Please install a polkit authentication agent for your desktop environment of choice to continue'
          })
        }
        throw error
      }
    })
  }
  