feat: permission changing (#5)
parent
b098e4cb99
commit
8fbda126e4
|
@ -185,3 +185,9 @@ export async function unarchive(path, name, override) {
|
||||||
const url = `${path}?action=${action}&destination=${to}&override=${override}`;
|
const url = `${path}?action=${action}&destination=${to}&override=${override}`;
|
||||||
return resourceAction(url, "PATCH");
|
return resourceAction(url, "PATCH");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function chmod(path, perms, recursive = false) {
|
||||||
|
const action = `chmod`;
|
||||||
|
const url = `${path}?action=${action}&permissions=${perms}&recursive=${recursive}`;
|
||||||
|
return resourceAction(url, "PATCH");
|
||||||
|
}
|
||||||
|
|
|
@ -41,6 +41,8 @@
|
||||||
<p class="modified">
|
<p class="modified">
|
||||||
<time :datetime="modified">{{ humanTime() }}</time>
|
<time :datetime="modified">{{ humanTime() }}</time>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p class="permissions">{{ permissions() }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -68,6 +70,7 @@ export default {
|
||||||
"url",
|
"url",
|
||||||
"type",
|
"type",
|
||||||
"size",
|
"size",
|
||||||
|
"mode",
|
||||||
"modified",
|
"modified",
|
||||||
"index",
|
"index",
|
||||||
"readOnly",
|
"readOnly",
|
||||||
|
@ -116,6 +119,26 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations(["addSelected", "removeSelected", "resetSelected"]),
|
...mapMutations(["addSelected", "removeSelected", "resetSelected"]),
|
||||||
|
permissions() {
|
||||||
|
let s = "";
|
||||||
|
if (this.isSymlink) {
|
||||||
|
s += "l";
|
||||||
|
} else if (this.isDir) {
|
||||||
|
s += "d";
|
||||||
|
} else {
|
||||||
|
s += "-";
|
||||||
|
}
|
||||||
|
s += (this.mode & 256) != 0 ? "r" : "-";
|
||||||
|
s += (this.mode & 128) != 0 ? "w" : "-";
|
||||||
|
s += (this.mode & 64) != 0 ? "x" : "-";
|
||||||
|
s += (this.mode & 32) != 0 ? "r" : "-";
|
||||||
|
s += (this.mode & 16) != 0 ? "w" : "-";
|
||||||
|
s += (this.mode & 8) != 0 ? "x" : "-";
|
||||||
|
s += (this.mode & 4) != 0 ? "r" : "-";
|
||||||
|
s += (this.mode & 2) != 0 ? "w" : "-";
|
||||||
|
s += (this.mode & 1) != 0 ? "x" : "-";
|
||||||
|
return s;
|
||||||
|
},
|
||||||
humanSize: function () {
|
humanSize: function () {
|
||||||
return filesize(this.size);
|
return filesize(this.size);
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,207 @@
|
||||||
|
<template>
|
||||||
|
<div class="card floating">
|
||||||
|
<div class="card-title">
|
||||||
|
<h2>{{ $t("prompts.permissions") }}</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-content">
|
||||||
|
<table class="permissions">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td>{{ $t("prompts.read") }}</td>
|
||||||
|
<td>{{ $t("prompts.write") }}</td>
|
||||||
|
<td>{{ $t("prompts.execute") }}</td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr class="permission-row">
|
||||||
|
<td>{{ $t("prompts.owner") }}</td>
|
||||||
|
<td>
|
||||||
|
<input type="checkbox" v-model="permissions.owner.read" />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="checkbox" v-model="permissions.owner.write" />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="checkbox" v-model="permissions.owner.execute" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="permission-row">
|
||||||
|
<td>{{ $t("prompts.group") }}</td>
|
||||||
|
<td>
|
||||||
|
<input type="checkbox" v-model="permissions.group.read" />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="checkbox" v-model="permissions.group.write" />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="checkbox" v-model="permissions.group.execute" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="permission-row">
|
||||||
|
<td>{{ $t("prompts.others") }}</td>
|
||||||
|
<td>
|
||||||
|
<input type="checkbox" v-model="permissions.others.read" />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="checkbox" v-model="permissions.others.write" />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="checkbox" v-model="permissions.others.execute" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p>
|
||||||
|
<code>{{ permModeString }} ({{ permMode.toString(8) }})</code>
|
||||||
|
</p>
|
||||||
|
<p v-if="dirSelected">
|
||||||
|
<input type="checkbox" v-model="recursive" />
|
||||||
|
{{ $t("prompts.recursive") }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-action">
|
||||||
|
<button
|
||||||
|
class="button button--flat button--grey"
|
||||||
|
@click="$store.commit('closeHovers')"
|
||||||
|
:aria-label="$t('buttons.cancel')"
|
||||||
|
:title="$t('buttons.cancel')"
|
||||||
|
>
|
||||||
|
{{ $t("buttons.cancel") }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button button--flat"
|
||||||
|
@click="chmod"
|
||||||
|
:disabled="loading"
|
||||||
|
:aria-label="$t('buttons.update')"
|
||||||
|
:title="$t('buttons.update')"
|
||||||
|
>
|
||||||
|
{{ $t("buttons.update") }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapState, mapGetters } from "vuex";
|
||||||
|
import { files as api } from "@/api";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "permissions",
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
recursive: false,
|
||||||
|
permissions: {
|
||||||
|
owner: {
|
||||||
|
read: false,
|
||||||
|
write: false,
|
||||||
|
execute: false,
|
||||||
|
},
|
||||||
|
group: {
|
||||||
|
read: false,
|
||||||
|
write: false,
|
||||||
|
execute: false,
|
||||||
|
},
|
||||||
|
others: {
|
||||||
|
read: false,
|
||||||
|
write: false,
|
||||||
|
execute: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
masks: {
|
||||||
|
permissions: 511,
|
||||||
|
owner: {
|
||||||
|
read: 256,
|
||||||
|
write: 128,
|
||||||
|
execute: 64,
|
||||||
|
},
|
||||||
|
group: {
|
||||||
|
read: 32,
|
||||||
|
write: 16,
|
||||||
|
execute: 8,
|
||||||
|
},
|
||||||
|
others: {
|
||||||
|
read: 4,
|
||||||
|
write: 2,
|
||||||
|
execute: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
loading: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState(["req", "selected"]),
|
||||||
|
...mapGetters(["isFiles", "isListing"]),
|
||||||
|
permMode() {
|
||||||
|
let mode = 0;
|
||||||
|
mode |= this.masks.owner.read * this.permissions.owner.read;
|
||||||
|
mode |= this.masks.owner.write * this.permissions.owner.write;
|
||||||
|
mode |= this.masks.owner.execute * this.permissions.owner.execute;
|
||||||
|
mode |= this.masks.group.read * this.permissions.group.read;
|
||||||
|
mode |= this.masks.group.write * this.permissions.group.write;
|
||||||
|
mode |= this.masks.group.execute * this.permissions.group.execute;
|
||||||
|
mode |= this.masks.others.read * this.permissions.others.read;
|
||||||
|
mode |= this.masks.others.write * this.permissions.others.write;
|
||||||
|
mode |= this.masks.others.execute * this.permissions.others.execute;
|
||||||
|
return mode;
|
||||||
|
},
|
||||||
|
permModeString() {
|
||||||
|
let perms = this.permMode;
|
||||||
|
let s = "";
|
||||||
|
s += (perms & this.masks.owner.read) != 0 ? "r" : "-";
|
||||||
|
s += (perms & this.masks.owner.write) != 0 ? "w" : "-";
|
||||||
|
s += (perms & this.masks.owner.execute) != 0 ? "x" : "-";
|
||||||
|
s += (perms & this.masks.group.read) != 0 ? "r" : "-";
|
||||||
|
s += (perms & this.masks.group.write) != 0 ? "w" : "-";
|
||||||
|
s += (perms & this.masks.group.execute) != 0 ? "x" : "-";
|
||||||
|
s += (perms & this.masks.others.read) != 0 ? "r" : "-";
|
||||||
|
s += (perms & this.masks.others.write) != 0 ? "w" : "-";
|
||||||
|
s += (perms & this.masks.others.execute) != 0 ? "x" : "-";
|
||||||
|
return s;
|
||||||
|
},
|
||||||
|
dirSelected() {
|
||||||
|
return this.req.items[this.selected[0]].isDir;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
let item = this.req.items[this.selected[0]];
|
||||||
|
let perms = item.mode & this.masks.permissions;
|
||||||
|
|
||||||
|
// OWNER PERMS
|
||||||
|
this.permissions.owner.read = (perms & this.masks.owner.read) != 0;
|
||||||
|
this.permissions.owner.write = (perms & this.masks.owner.write) != 0;
|
||||||
|
this.permissions.owner.execute = (perms & this.masks.owner.execute) != 0;
|
||||||
|
// GROUP PERMS
|
||||||
|
this.permissions.group.read = (perms & this.masks.group.read) != 0;
|
||||||
|
this.permissions.group.write = (perms & this.masks.group.write) != 0;
|
||||||
|
this.permissions.group.execute = (perms & this.masks.group.execute) != 0;
|
||||||
|
// OTHERS PERMS
|
||||||
|
this.permissions.others.read = (perms & this.masks.others.read) != 0;
|
||||||
|
this.permissions.others.write = (perms & this.masks.others.write) != 0;
|
||||||
|
this.permissions.others.execute = (perms & this.masks.others.execute) != 0;
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
cancel: function () {
|
||||||
|
this.$store.commit("closeHovers");
|
||||||
|
},
|
||||||
|
chmod: async function () {
|
||||||
|
let item = this.req.items[this.selected[0]];
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.loading = true;
|
||||||
|
await api.chmod(item.url, this.permMode, this.recursive);
|
||||||
|
|
||||||
|
this.$store.commit("setReload", true);
|
||||||
|
} catch (e) {
|
||||||
|
this.$showError(e);
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$store.commit("closeHovers");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -14,6 +14,7 @@ import Download from "./Download";
|
||||||
import Move from "./Move";
|
import Move from "./Move";
|
||||||
import Archive from "./Archive";
|
import Archive from "./Archive";
|
||||||
import Unarchive from "./Unarchive";
|
import Unarchive from "./Unarchive";
|
||||||
|
import Permissions from "./Permissions";
|
||||||
import Copy from "./Copy";
|
import Copy from "./Copy";
|
||||||
import NewFile from "./NewFile";
|
import NewFile from "./NewFile";
|
||||||
import NewDir from "./NewDir";
|
import NewDir from "./NewDir";
|
||||||
|
@ -35,6 +36,7 @@ export default {
|
||||||
Move,
|
Move,
|
||||||
Archive,
|
Archive,
|
||||||
Unarchive,
|
Unarchive,
|
||||||
|
Permissions,
|
||||||
Copy,
|
Copy,
|
||||||
Share,
|
Share,
|
||||||
NewFile,
|
NewFile,
|
||||||
|
@ -97,6 +99,7 @@ export default {
|
||||||
"move",
|
"move",
|
||||||
"archive",
|
"archive",
|
||||||
"unarchive",
|
"unarchive",
|
||||||
|
"permissions",
|
||||||
"copy",
|
"copy",
|
||||||
"newFile",
|
"newFile",
|
||||||
"newDir",
|
"newDir",
|
||||||
|
|
|
@ -155,7 +155,19 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
#listing.list .item .size {
|
#listing.list .item .size {
|
||||||
width: 25%;
|
width: 15%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#listing.list .item .modified {
|
||||||
|
width: 20%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#listing .item .permissions {
|
||||||
|
font-family: monospace, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
#listing.mosaic .item .permissions {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#listing .item.header {
|
#listing .item.header {
|
||||||
|
@ -247,6 +259,11 @@
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#listing .item[aria-selected=true] .symlink-icon {
|
||||||
|
color: var(--blue);
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
#listing.mosaic .item .symlink-icon.dir {
|
#listing.mosaic .item .symlink-icon.dir {
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
left: .45em;
|
left: .45em;
|
||||||
|
|
|
@ -8,6 +8,9 @@
|
||||||
main {
|
main {
|
||||||
width: calc(100% - 13em)
|
width: calc(100% - 13em)
|
||||||
}
|
}
|
||||||
|
#listing.list .item .permissions {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 736px) {
|
@media (max-width: 736px) {
|
||||||
|
@ -20,6 +23,9 @@
|
||||||
#listing.list .item .name {
|
#listing.list .item .name {
|
||||||
width: 60%;
|
width: 60%;
|
||||||
}
|
}
|
||||||
|
#listing.list .item .permissions {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
#more {
|
#more {
|
||||||
display: inherit
|
display: inherit
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
"archive": "Archive",
|
"archive": "Archive",
|
||||||
"unarchive": "Unarchive",
|
"unarchive": "Unarchive",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
|
"permissions": "Permissions",
|
||||||
"close": "Close",
|
"close": "Close",
|
||||||
"copy": "Copy",
|
"copy": "Copy",
|
||||||
"copyFile": "Copy file",
|
"copyFile": "Copy file",
|
||||||
|
@ -116,6 +117,14 @@
|
||||||
"permanent": "Permanent",
|
"permanent": "Permanent",
|
||||||
"prompts": {
|
"prompts": {
|
||||||
"unsavedChanges": "Changes that you made may not be saved. Leave page?",
|
"unsavedChanges": "Changes that you made may not be saved. Leave page?",
|
||||||
|
"permissions": "Permissions",
|
||||||
|
"read": "Read",
|
||||||
|
"write": "Write",
|
||||||
|
"execute": "Execute",
|
||||||
|
"owner": "Owner",
|
||||||
|
"group": "Group",
|
||||||
|
"others": "Others",
|
||||||
|
"recursive": "Recursive",
|
||||||
"archive": "Archive",
|
"archive": "Archive",
|
||||||
"archiveMessage": "Choose archive name and format:",
|
"archiveMessage": "Choose archive name and format:",
|
||||||
"unarchive": "Unarchive",
|
"unarchive": "Unarchive",
|
||||||
|
|
|
@ -37,6 +37,13 @@
|
||||||
:label="$t('buttons.moveFile')"
|
:label="$t('buttons.moveFile')"
|
||||||
show="move"
|
show="move"
|
||||||
/>
|
/>
|
||||||
|
<action
|
||||||
|
v-if="headerButtons.permissions"
|
||||||
|
id="permissions-button"
|
||||||
|
icon="lock"
|
||||||
|
:label="$t('buttons.permissions')"
|
||||||
|
show="permissions"
|
||||||
|
/>
|
||||||
<action
|
<action
|
||||||
v-if="headerButtons.archive"
|
v-if="headerButtons.archive"
|
||||||
id="archive-button"
|
id="archive-button"
|
||||||
|
@ -217,6 +224,7 @@
|
||||||
v-bind:link="item.link"
|
v-bind:link="item.link"
|
||||||
v-bind:isDir="item.isDir"
|
v-bind:isDir="item.isDir"
|
||||||
v-bind:url="item.url"
|
v-bind:url="item.url"
|
||||||
|
v-bind:mode="item.mode"
|
||||||
v-bind:modified="item.modified"
|
v-bind:modified="item.modified"
|
||||||
v-bind:type="item.type"
|
v-bind:type="item.type"
|
||||||
v-bind:size="item.size"
|
v-bind:size="item.size"
|
||||||
|
@ -235,6 +243,7 @@
|
||||||
v-bind:link="item.link"
|
v-bind:link="item.link"
|
||||||
v-bind:isDir="item.isDir"
|
v-bind:isDir="item.isDir"
|
||||||
v-bind:url="item.url"
|
v-bind:url="item.url"
|
||||||
|
v-bind:mode="item.mode"
|
||||||
v-bind:modified="item.modified"
|
v-bind:modified="item.modified"
|
||||||
v-bind:type="item.type"
|
v-bind:type="item.type"
|
||||||
v-bind:size="item.size"
|
v-bind:size="item.size"
|
||||||
|
@ -384,6 +393,7 @@ export default {
|
||||||
share: this.selectedCount === 1 && this.user.perm.share,
|
share: this.selectedCount === 1 && this.user.perm.share,
|
||||||
move: this.selectedCount > 0 && this.user.perm.rename,
|
move: this.selectedCount > 0 && this.user.perm.rename,
|
||||||
copy: this.selectedCount > 0 && this.user.perm.create,
|
copy: this.selectedCount > 0 && this.user.perm.create,
|
||||||
|
permissions: this.selectedCount === 1 && this.user.perm.modify,
|
||||||
archive: this.selectedCount > 0 && this.user.perm.create,
|
archive: this.selectedCount > 0 && this.user.perm.create,
|
||||||
unarchive: this.selectedCount === 1 && this.onlyArchivesSelected,
|
unarchive: this.selectedCount === 1 && this.onlyArchivesSelected,
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/mholt/archiver"
|
"github.com/mholt/archiver"
|
||||||
|
@ -211,6 +212,12 @@ func resourcePatchHandler(fileCache FileCache) handleFunc {
|
||||||
src := r.URL.Path
|
src := r.URL.Path
|
||||||
dst := r.URL.Query().Get("destination")
|
dst := r.URL.Query().Get("destination")
|
||||||
action := r.URL.Query().Get("action")
|
action := r.URL.Query().Get("action")
|
||||||
|
|
||||||
|
if action == "chmod" {
|
||||||
|
err := chmodActionHandler(r, d)
|
||||||
|
return errToStatus(err), err
|
||||||
|
}
|
||||||
|
|
||||||
dst, err := url.QueryUnescape(dst)
|
dst, err := url.QueryUnescape(dst)
|
||||||
if !d.Check(src) || !d.Check(dst) {
|
if !d.Check(src) || !d.Check(dst) {
|
||||||
return http.StatusForbidden, nil
|
return http.StatusForbidden, nil
|
||||||
|
@ -229,7 +236,7 @@ func resourcePatchHandler(fileCache FileCache) handleFunc {
|
||||||
|
|
||||||
override := r.URL.Query().Get("override") == "true"
|
override := r.URL.Query().Get("override") == "true"
|
||||||
rename := r.URL.Query().Get("rename") == "true"
|
rename := r.URL.Query().Get("rename") == "true"
|
||||||
unarchive := r.URL.Query().Get("action") == "unarchive"
|
unarchive := action == "unarchive"
|
||||||
if !override && !rename && !unarchive {
|
if !override && !rename && !unarchive {
|
||||||
if _, err = d.user.Fs.Stat(dst); err == nil {
|
if _, err = d.user.Fs.Stat(dst); err == nil {
|
||||||
return http.StatusConflict, nil
|
return http.StatusConflict, nil
|
||||||
|
@ -339,7 +346,6 @@ func patchAction(ctx context.Context, action, src, dst string, d *data, fileCach
|
||||||
src = d.user.FullPath(path.Clean("/" + src))
|
src = d.user.FullPath(path.Clean("/" + src))
|
||||||
dst = d.user.FullPath(path.Clean("/" + dst))
|
dst = d.user.FullPath(path.Clean("/" + dst))
|
||||||
|
|
||||||
// THIS COULD BE VUNERABLE TO https://github.com/snyk/zip-slip-vulnerability
|
|
||||||
err := archiver.Unarchive(src, dst)
|
err := archiver.Unarchive(src, dst)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.ErrInvalidRequestParams
|
return errors.ErrInvalidRequestParams
|
||||||
|
@ -469,3 +475,38 @@ func parseArchiver(algo string) (string, archiver.Archiver, error) {
|
||||||
return "", nil, fmt.Errorf("format not implemented")
|
return "", nil, fmt.Errorf("format not implemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func chmodActionHandler(r *http.Request, d *data) error {
|
||||||
|
target := r.URL.Path
|
||||||
|
recursive := r.URL.Query().Get("recursive") == "true"
|
||||||
|
perms := r.URL.Query().Get("permissions")
|
||||||
|
|
||||||
|
if !d.user.Perm.Modify {
|
||||||
|
return errors.ErrPermissionDenied
|
||||||
|
}
|
||||||
|
|
||||||
|
if !d.Check(target) || target == "/" {
|
||||||
|
return errors.ErrPermissionDenied
|
||||||
|
}
|
||||||
|
|
||||||
|
mode, err := strconv.ParseUint(perms, 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return errors.ErrInvalidRequestParams
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := d.user.Fs.Stat(target)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if recursive && info.IsDir() {
|
||||||
|
return afero.Walk(d.user.Fs, target, func(name string, info os.FileInfo, err error) error {
|
||||||
|
if err == nil {
|
||||||
|
err = d.user.Fs.Chmod(name, os.FileMode(mode))
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return d.user.Fs.Chmod(target, os.FileMode(mode))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue