Chmod / Permissions Calculator

Everything happens locally in your browser. Nothing you type here is sent anywhere, logged, or stored, it disappears the moment you close the tab.

Read Write Execute
Owner
Group
Other
Symbolic
Command

Reading Permission Bits

Every file and directory on a Linux system carries three permissions each for three groups: what the owner can do, what the group can do, and what everyone else can do. Each of those three slots holds some combination of read, write, and execute, which is why permissions get expressed as either a nine-character string like rwxr-xr-- or a three-digit octal number like 754, they're two notations for the exact same nine yes/no switches.

Owner, Group, Other, and the Three Permissions

Read (4) lets you view a file's contents or list a directory's entries. Write (2) lets you modify a file or add/remove entries in a directory. Execute (1) lets you run a file as a program, or, for a directory, actually enter it with cd. Add whichever of those apply for a given slot, and you get that slot's octal digit, a slot with read and execute but no write is 4 + 1 = 5. Do that three times, once each for owner, group, and other, and you have the full three-digit number.

Why Directories Need the Execute Bit

This is the part that trips people up first: on a directory, execute doesn't mean "run it," it means "you're allowed to enter it and access what's inside," even to just ls a file you already know the name of. A directory with read but no execute lets you see filenames but not touch anything in it. A directory with execute but no read lets you access a file inside it if you already know its exact name, but not list what's there. Directories you actually want to use need both, which is why 755 (not 655) is the standard for a directory you're sharing read access to.

Setuid, Setgid, and the Sticky Bit

Three special bits sit above the normal nine, and they're rare enough in day-to-day use that it's worth knowing what they do before you set one. Setuid on an executable runs it as the file's owner rather than whoever launched it, useful and dangerous in equal measure, this is how passwd can update a system file a regular user otherwise couldn't touch. Setgid on a directory makes new files created inside it inherit the directory's group instead of the creating user's group, handy for a shared team folder. The sticky bit on a directory (classically on /tmp) means users can only delete or rename their own files inside it, even if they technically have write access to the directory as a whole.

Common Permissions Worth Memorizing

644 is the default for a regular file you own: you can read and write it, everyone else can only read it. 755 is the same idea for directories and scripts, add execute across the board so the directory can be entered or the script can be run. 600 is for anything private and sensitive, an SSH private key, a credentials file, only you can read or write it, nobody else gets anything. 700 is the directory version of that, your own .ssh folder should be exactly this. If a tool is refusing to use a key or config file with a permissions error, one of these four numbers is almost always the fix.

Copy the Code

Plain HTML, CSS, and JavaScript, no dependencies, no build step, nothing phoned home. Comments walk through what each block is doing, so it's easy to adapt rather than just paste and forget.

<!-- Chmod / Permissions Calculator -- copy this whole block into any HTML page. -->
<!-- No build step, no dependencies -- plain HTML, CSS, and JS. -->
<!-- Everything runs locally: nothing is ever sent anywhere. -->

<div id="chmod-calculator">
  <div class="chmod-octal-field">
    <label for="chmodOctalInput">Octal</label>
    <input type="text" id="chmodOctalInput" maxlength="4" value="754" autocomplete="off" spellcheck="false">
  </div>
  <p id="chmodError"></p>

  <table>
    <tr><th></th><th>Read</th><th>Write</th><th>Execute</th></tr>
    <tr><td>Owner</td><td><input type="checkbox" id="chmodOwnerR"></td><td><input type="checkbox" id="chmodOwnerW"></td><td><input type="checkbox" id="chmodOwnerX"></td></tr>
    <tr><td>Group</td><td><input type="checkbox" id="chmodGroupR"></td><td><input type="checkbox" id="chmodGroupW"></td><td><input type="checkbox" id="chmodGroupX"></td></tr>
    <tr><td>Other</td><td><input type="checkbox" id="chmodOtherR"></td><td><input type="checkbox" id="chmodOtherW"></td><td><input type="checkbox" id="chmodOtherX"></td></tr>
  </table>

  <label><input type="checkbox" id="chmodSetuid"> setuid</label>
  <label><input type="checkbox" id="chmodSetgid"> setgid</label>
  <label><input type="checkbox" id="chmodSticky"> sticky bit</label>

  <div>Symbolic: <span id="chmodSymbolic">&mdash;</span></div>
  <div>Command: <span id="chmodCommand">&mdash;</span></div>
</div>

<script>
(function () {
  var octalInput = document.getElementById('chmodOctalInput');
  var error = document.getElementById('chmodError');
  var symbolicEl = document.getElementById('chmodSymbolic');
  var commandEl = document.getElementById('chmodCommand');

  var boxes = {
    ownerR: document.getElementById('chmodOwnerR'), ownerW: document.getElementById('chmodOwnerW'), ownerX: document.getElementById('chmodOwnerX'),
    groupR: document.getElementById('chmodGroupR'), groupW: document.getElementById('chmodGroupW'), groupX: document.getElementById('chmodGroupX'),
    otherR: document.getElementById('chmodOtherR'), otherW: document.getElementById('chmodOtherW'), otherX: document.getElementById('chmodOtherX'),
    setuid: document.getElementById('chmodSetuid'), setgid: document.getElementById('chmodSetgid'), sticky: document.getElementById('chmodSticky')
  };

  var syncing = false;

  function whoOctal(who) {
    return (boxes[who + 'R'].checked ? 4 : 0) + (boxes[who + 'W'].checked ? 2 : 0) + (boxes[who + 'X'].checked ? 1 : 0);
  }

  function specialOctal() {
    return (boxes.setuid.checked ? 4 : 0) + (boxes.setgid.checked ? 2 : 0) + (boxes.sticky.checked ? 1 : 0);
  }

  function specialChar(execChecked, specialChecked, letter) {
    if (specialChecked) return execChecked ? letter : letter.toUpperCase();
    return execChecked ? 'x' : '-';
  }

  function render(fromOctal) {
    var owner = whoOctal('owner'), group = whoOctal('group'), other = whoOctal('other'), special = specialOctal();
    var octalString = (special ? String(special) : '') + owner + group + other;

    if (!fromOctal) {
      syncing = true;
      octalInput.value = octalString;
      syncing = false;
    }

    var sym =
      (boxes.ownerR.checked ? 'r' : '-') + (boxes.ownerW.checked ? 'w' : '-') + specialChar(boxes.ownerX.checked, boxes.setuid.checked, 's') +
      (boxes.groupR.checked ? 'r' : '-') + (boxes.groupW.checked ? 'w' : '-') + specialChar(boxes.groupX.checked, boxes.setgid.checked, 's') +
      (boxes.otherR.checked ? 'r' : '-') + (boxes.otherW.checked ? 'w' : '-') + specialChar(boxes.otherX.checked, boxes.sticky.checked, 't');

    symbolicEl.textContent = sym;
    commandEl.textContent = 'chmod ' + octalString + ' filename';
  }

  function applyOctal(value) {
    var trimmed = value.trim();
    if (!/^[0-7]{3,4}$/.test(trimmed)) {
      error.textContent = trimmed ? 'Enter 3 or 4 octal digits, each 0-7.' : '';
      return;
    }
    error.textContent = '';
    var digits = trimmed.length === 4 ? trimmed : '0' + trimmed;
    var special = Number(digits[0]), owner = Number(digits[1]), group = Number(digits[2]), other = Number(digits[3]);

    boxes.setuid.checked = !!(special & 4);
    boxes.setgid.checked = !!(special & 2);
    boxes.sticky.checked = !!(special & 1);
    boxes.ownerR.checked = !!(owner & 4); boxes.ownerW.checked = !!(owner & 2); boxes.ownerX.checked = !!(owner & 1);
    boxes.groupR.checked = !!(group & 4); boxes.groupW.checked = !!(group & 2); boxes.groupX.checked = !!(group & 1);
    boxes.otherR.checked = !!(other & 4); boxes.otherW.checked = !!(other & 2); boxes.otherX.checked = !!(other & 1);

    render(true);
  }

  Object.keys(boxes).forEach(function (key) {
    boxes[key].addEventListener('change', function () { render(false); });
  });
  octalInput.addEventListener('input', function () {
    if (!syncing) applyOctal(octalInput.value);
  });

  applyOctal(octalInput.value);
})();
</script>